mirror of
https://github.com/home-assistant/core.git
synced 2026-09-25 07:51:46 -05:00
Fix swallowed exceptions in roomba action handlers (#181042)
This commit is contained in:
@@ -89,6 +89,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"invalid_fan_speed": {
|
||||
"message": "Invalid fan speed {fan_speed}. Expected one of: {fan_speeds}."
|
||||
},
|
||||
"invalid_fan_speed_format": {
|
||||
"message": "Invalid fan speed {fan_speed}. Expected the format behavior-spray_amount, for example Standard-1."
|
||||
},
|
||||
"invalid_mop_behavior": {
|
||||
"message": "Invalid mop behavior {behavior}. Expected one of: {behaviors}."
|
||||
},
|
||||
"invalid_spray_amount": {
|
||||
"message": "Invalid spray amount {spray_amount}. Expected one of: {spray_amounts}."
|
||||
},
|
||||
"spray_amount_not_a_number": {
|
||||
"message": "Invalid spray amount {spray_amount}. Expected a whole number."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
|
||||
@@ -11,11 +11,13 @@ from homeassistant.components.vacuum import (
|
||||
VacuumEntityFeature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||
|
||||
from . import roomba_reported_state
|
||||
from .const import DOMAIN
|
||||
from .entity import IRobotEntity
|
||||
from .models import RoombaConfigEntry
|
||||
|
||||
@@ -317,8 +319,14 @@ class RoombaVacuumCarpetBoost(RoombaVacuum):
|
||||
high_perf = True
|
||||
carpet_boost = False
|
||||
else:
|
||||
_LOGGER.error("No such fan speed available: %s", fan_speed)
|
||||
return
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_fan_speed",
|
||||
translation_placeholders={
|
||||
"fan_speed": fan_speed,
|
||||
"fan_speeds": ", ".join(FAN_SPEEDS),
|
||||
},
|
||||
)
|
||||
|
||||
# The set_preference method does only accept string values
|
||||
def _set_fan_speed_preferences() -> None:
|
||||
@@ -371,30 +379,36 @@ class BraavaJet(IRobotVacuum):
|
||||
spray = int(split[1])
|
||||
if behavior.capitalize() in BRAAVA_MOP_BEHAVIORS:
|
||||
behavior = behavior.capitalize()
|
||||
# pylint: disable-next=home-assistant-action-swallowed-exception
|
||||
except IndexError:
|
||||
_LOGGER.error(
|
||||
"Fan speed error: expected {behavior}-{spray_amount}, got '%s'",
|
||||
fan_speed,
|
||||
)
|
||||
return
|
||||
except ValueError:
|
||||
_LOGGER.error("Spray amount error: expected integer, got '%s'", split[1])
|
||||
return
|
||||
except IndexError as err:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_fan_speed_format",
|
||||
translation_placeholders={"fan_speed": fan_speed},
|
||||
) from err
|
||||
except ValueError as err:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="spray_amount_not_a_number",
|
||||
translation_placeholders={"spray_amount": split[1]},
|
||||
) from err
|
||||
if behavior not in BRAAVA_MOP_BEHAVIORS:
|
||||
_LOGGER.error(
|
||||
"Mop behavior error: expected one of %s, got '%s'",
|
||||
str(BRAAVA_MOP_BEHAVIORS),
|
||||
behavior,
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_mop_behavior",
|
||||
translation_placeholders={
|
||||
"behavior": behavior,
|
||||
"behaviors": ", ".join(BRAAVA_MOP_BEHAVIORS),
|
||||
},
|
||||
)
|
||||
return
|
||||
if spray not in BRAAVA_SPRAY_AMOUNT:
|
||||
_LOGGER.error(
|
||||
"Spray amount error: expected one of %s, got '%d'",
|
||||
str(BRAAVA_SPRAY_AMOUNT),
|
||||
spray,
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_spray_amount",
|
||||
translation_placeholders={
|
||||
"spray_amount": str(spray),
|
||||
"spray_amounts": ", ".join(str(s) for s in BRAAVA_SPRAY_AMOUNT),
|
||||
},
|
||||
)
|
||||
return
|
||||
|
||||
overlap = 0
|
||||
if behavior == MOP_STANDARD:
|
||||
|
||||
@@ -4,15 +4,29 @@ from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.vacuum import VacuumActivity
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.components.vacuum import (
|
||||
ATTR_FAN_SPEED,
|
||||
DOMAIN as VACUUM_DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
VacuumActivity,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
ENTITY_ID = "vacuum.test_roomba"
|
||||
|
||||
|
||||
async def _setup(hass: HomeAssistant, mock_config_entry: MockConfigEntry) -> None:
|
||||
"""Set up the vacuum platform only."""
|
||||
with patch("homeassistant.components.roomba.PLATFORMS", [Platform.VACUUM]):
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("phase", "cycle", "expected"),
|
||||
[
|
||||
@@ -52,3 +66,83 @@ async def test_vacuum_activity(
|
||||
state = hass.states.get(ENTITY_ID)
|
||||
assert state is not None
|
||||
assert state.state == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("fan_speed", "translation_key"),
|
||||
[
|
||||
# Missing the "-<spray amount>" half entirely.
|
||||
("Standard", "invalid_fan_speed_format"),
|
||||
# Spray amount present but not a number.
|
||||
("Standard-x", "spray_amount_not_a_number"),
|
||||
# Well-formed, but the behavior is not one we support.
|
||||
("Bogus-1", "invalid_mop_behavior"),
|
||||
# Well-formed, but the spray amount is out of range.
|
||||
("Standard-9", "invalid_spray_amount"),
|
||||
],
|
||||
)
|
||||
async def test_braava_set_fan_speed_invalid(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_roomba: AsyncMock,
|
||||
fan_speed: str,
|
||||
translation_key: str,
|
||||
) -> None:
|
||||
"""Test that invalid Braava fan speeds raise instead of being swallowed."""
|
||||
mock_roomba.master_state["state"]["reported"]["detectedPad"] = "reusableWet"
|
||||
|
||||
await _setup(hass, mock_config_entry)
|
||||
|
||||
with pytest.raises(ServiceValidationError) as err:
|
||||
await hass.services.async_call(
|
||||
VACUUM_DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_FAN_SPEED: fan_speed},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert err.value.translation_domain == "roomba"
|
||||
assert err.value.translation_key == translation_key
|
||||
|
||||
|
||||
async def test_carpet_boost_set_fan_speed_invalid(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_roomba: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that an unknown carpet-boost fan speed raises instead of being swallowed."""
|
||||
mock_roomba.master_state["state"]["reported"]["cap"]["carpetBoost"] = 1
|
||||
|
||||
await _setup(hass, mock_config_entry)
|
||||
|
||||
with pytest.raises(ServiceValidationError) as err:
|
||||
await hass.services.async_call(
|
||||
VACUUM_DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_FAN_SPEED: "Turbo"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert err.value.translation_domain == "roomba"
|
||||
assert err.value.translation_key == "invalid_fan_speed"
|
||||
|
||||
|
||||
async def test_carpet_boost_set_fan_speed_valid(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_roomba: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that a valid fan speed still sets the preferences."""
|
||||
mock_roomba.master_state["state"]["reported"]["cap"]["carpetBoost"] = 1
|
||||
|
||||
await _setup(hass, mock_config_entry)
|
||||
|
||||
await hass.services.async_call(
|
||||
VACUUM_DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_FAN_SPEED: "eco"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_roomba.set_preference.assert_any_call("carpetBoost", "False")
|
||||
mock_roomba.set_preference.assert_any_call("vacHigh", "False")
|
||||
|
||||
Reference in New Issue
Block a user