Use backend-provided fan speed presets for Xiaomi vacuums, bum… (#32850)
* Use backend-provided fan speed presets for Xiaomi vacuums This needs input from Xiaomi vacuum owners to verify that it does not break anything. I have personally tested this on rockrobo v1 (old mapping). Related issues/PRs: home-assistant/core#32821 home-assistant/core#31268 home-assistant/core#27268 This is a WIP as it requires a new upstream release. The PR is https://github.com/rytilahti/python-miio/pull/643 * Bump version requirement for 0.5.0 * Bump requirements_test_all.txt, too * Fix linting; missing setup.cfg on local checkout caused wrong settings for black.. * Add tests for both fan speed types * Remove useless else.. * bump python-miio to 0.5.0.1 due to broken 0.5.0 packaging
This commit is contained in:
parent
cb058ff6c0
commit
e64104300f
5 changed files with 110 additions and 60 deletions
|
@ -2,7 +2,7 @@
|
|||
"domain": "xiaomi_miio",
|
||||
"name": "Xiaomi miio",
|
||||
"documentation": "https://www.home-assistant.io/integrations/xiaomi_miio",
|
||||
"requirements": ["construct==2.9.45", "python-miio==0.4.8"],
|
||||
"requirements": ["construct==2.9.45", "python-miio==0.5.0.1"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@rytilahti", "@syssi"]
|
||||
}
|
||||
|
|
|
@ -60,8 +60,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
FAN_SPEEDS = {"Silent": 38, "Standard": 60, "Medium": 77, "Turbo": 90, "Gentle": 105}
|
||||
|
||||
ATTR_CLEAN_START = "clean_start"
|
||||
ATTR_CLEAN_STOP = "clean_stop"
|
||||
ATTR_CLEANING_TIME = "cleaning_time"
|
||||
|
@ -246,6 +244,8 @@ class MiroboVacuum(StateVacuumDevice):
|
|||
self.clean_history = None
|
||||
self.dnd_state = None
|
||||
self.last_clean = None
|
||||
self._fan_speeds = None
|
||||
self._fan_speeds_reverse = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -281,14 +281,17 @@ class MiroboVacuum(StateVacuumDevice):
|
|||
"""Return the fan speed of the vacuum cleaner."""
|
||||
if self.vacuum_state is not None:
|
||||
speed = self.vacuum_state.fanspeed
|
||||
if speed in FAN_SPEEDS.values():
|
||||
return [key for key, value in FAN_SPEEDS.items() if value == speed][0]
|
||||
if speed in self._fan_speeds_reverse:
|
||||
return self._fan_speeds_reverse[speed]
|
||||
|
||||
_LOGGER.debug("Unable to find reverse for %s", speed)
|
||||
|
||||
return speed
|
||||
|
||||
@property
|
||||
def fan_speed_list(self):
|
||||
"""Get the list of available fan speed steps of the vacuum cleaner."""
|
||||
return list(sorted(FAN_SPEEDS.keys(), key=lambda s: FAN_SPEEDS[s]))
|
||||
return list(self._fan_speeds)
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
@ -372,8 +375,8 @@ class MiroboVacuum(StateVacuumDevice):
|
|||
|
||||
async def async_set_fan_speed(self, fan_speed, **kwargs):
|
||||
"""Set fan speed."""
|
||||
if fan_speed.capitalize() in FAN_SPEEDS:
|
||||
fan_speed = FAN_SPEEDS[fan_speed.capitalize()]
|
||||
if fan_speed in self._fan_speeds:
|
||||
fan_speed = self._fan_speeds[fan_speed]
|
||||
else:
|
||||
try:
|
||||
fan_speed = int(fan_speed)
|
||||
|
@ -453,6 +456,9 @@ class MiroboVacuum(StateVacuumDevice):
|
|||
state = self._vacuum.status()
|
||||
self.vacuum_state = state
|
||||
|
||||
self._fan_speeds = self._vacuum.fan_speed_presets()
|
||||
self._fan_speeds_reverse = {v: k for k, v in self._fan_speeds.items()}
|
||||
|
||||
self.consumable_state = self._vacuum.consumable_status()
|
||||
self.clean_history = self._vacuum.clean_history()
|
||||
self.last_clean = self._vacuum.last_clean_details()
|
||||
|
|
|
@ -1638,7 +1638,7 @@ python-juicenet==0.1.6
|
|||
# python-lirc==1.2.3
|
||||
|
||||
# homeassistant.components.xiaomi_miio
|
||||
python-miio==0.4.8
|
||||
python-miio==0.5.0.1
|
||||
|
||||
# homeassistant.components.mpd
|
||||
python-mpd2==1.0.0
|
||||
|
|
|
@ -620,7 +620,7 @@ python-forecastio==1.4.0
|
|||
python-izone==1.1.2
|
||||
|
||||
# homeassistant.components.xiaomi_miio
|
||||
python-miio==0.4.8
|
||||
python-miio==0.5.0.1
|
||||
|
||||
# homeassistant.components.nest
|
||||
python-nest==4.1.0
|
||||
|
|
|
@ -100,6 +100,36 @@ def mirobo_is_got_error_fixture():
|
|||
yield mock_vacuum
|
||||
|
||||
|
||||
old_fanspeeds = {
|
||||
"Silent": 38,
|
||||
"Standard": 60,
|
||||
"Medium": 77,
|
||||
"Turbo": 90,
|
||||
}
|
||||
new_fanspeeds = {
|
||||
"Silent": 101,
|
||||
"Standard": 102,
|
||||
"Medium": 103,
|
||||
"Turbo": 104,
|
||||
"Gentle": 105,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_mirobo_fanspeeds", params=[old_fanspeeds, new_fanspeeds])
|
||||
def mirobo_old_speeds_fixture(request):
|
||||
"""Fixture for testing both types of fanspeeds."""
|
||||
mock_vacuum = mock.MagicMock()
|
||||
mock_vacuum.status().battery = 32
|
||||
mock_vacuum.fan_speed_presets.return_value = request.param
|
||||
mock_vacuum.status().fanspeed = list(request.param.values())[0]
|
||||
|
||||
with mock.patch(
|
||||
"homeassistant.components.xiaomi_miio.vacuum.Vacuum"
|
||||
) as mock_vaccum_cls:
|
||||
mock_vaccum_cls.return_value = mock_vacuum
|
||||
yield mock_vacuum
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_mirobo_is_on")
|
||||
def mirobo_is_on_fixture():
|
||||
"""Mock mock_mirobo."""
|
||||
|
@ -204,14 +234,6 @@ async def test_xiaomi_vacuum_services(hass, caplog, mock_mirobo_is_got_error):
|
|||
assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-80"
|
||||
assert state.attributes.get(ATTR_CLEANING_TIME) == 155
|
||||
assert state.attributes.get(ATTR_CLEANED_AREA) == 123
|
||||
assert state.attributes.get(ATTR_FAN_SPEED) == "Silent"
|
||||
assert state.attributes.get(ATTR_FAN_SPEED_LIST) == [
|
||||
"Silent",
|
||||
"Standard",
|
||||
"Medium",
|
||||
"Turbo",
|
||||
"Gentle",
|
||||
]
|
||||
assert state.attributes.get(ATTR_MAIN_BRUSH_LEFT) == 12
|
||||
assert state.attributes.get(ATTR_SIDE_BRUSH_LEFT) == 12
|
||||
assert state.attributes.get(ATTR_FILTER_LEFT) == 12
|
||||
|
@ -257,40 +279,6 @@ async def test_xiaomi_vacuum_services(hass, caplog, mock_mirobo_is_got_error):
|
|||
mock_mirobo_is_got_error.assert_has_calls(STATUS_CALLS, any_order=True)
|
||||
mock_mirobo_is_got_error.reset_mock()
|
||||
|
||||
# Set speed service:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{"entity_id": entity_id, "fan_speed": 60},
|
||||
blocking=True,
|
||||
)
|
||||
mock_mirobo_is_got_error.assert_has_calls(
|
||||
[mock.call.set_fan_speed(60)], any_order=True
|
||||
)
|
||||
mock_mirobo_is_got_error.assert_has_calls(STATUS_CALLS, any_order=True)
|
||||
mock_mirobo_is_got_error.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{"entity_id": entity_id, "fan_speed": "Medium"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_mirobo_is_got_error.assert_has_calls(
|
||||
[mock.call.set_fan_speed(77)], any_order=True
|
||||
)
|
||||
mock_mirobo_is_got_error.assert_has_calls(STATUS_CALLS, any_order=True)
|
||||
mock_mirobo_is_got_error.reset_mock()
|
||||
|
||||
assert "ERROR" not in caplog.text
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{"entity_id": entity_id, "fan_speed": "invent"},
|
||||
blocking=True,
|
||||
)
|
||||
assert "ERROR" in caplog.text
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SEND_COMMAND,
|
||||
|
@ -346,14 +334,6 @@ async def test_xiaomi_specific_services(hass, caplog, mock_mirobo_is_on):
|
|||
assert state.attributes.get(ATTR_BATTERY_ICON) == "mdi:battery-30"
|
||||
assert state.attributes.get(ATTR_CLEANING_TIME) == 175
|
||||
assert state.attributes.get(ATTR_CLEANED_AREA) == 133
|
||||
assert state.attributes.get(ATTR_FAN_SPEED) == 99
|
||||
assert state.attributes.get(ATTR_FAN_SPEED_LIST) == [
|
||||
"Silent",
|
||||
"Standard",
|
||||
"Medium",
|
||||
"Turbo",
|
||||
"Gentle",
|
||||
]
|
||||
assert state.attributes.get(ATTR_MAIN_BRUSH_LEFT) == 11
|
||||
assert state.attributes.get(ATTR_SIDE_BRUSH_LEFT) == 11
|
||||
assert state.attributes.get(ATTR_FILTER_LEFT) == 11
|
||||
|
@ -409,3 +389,67 @@ async def test_xiaomi_specific_services(hass, caplog, mock_mirobo_is_on):
|
|||
)
|
||||
mock_mirobo_is_on.assert_has_calls(STATUS_CALLS, any_order=True)
|
||||
mock_mirobo_is_on.reset_mock()
|
||||
|
||||
|
||||
async def test_xiaomi_vacuum_fanspeeds(hass, caplog, mock_mirobo_fanspeeds):
|
||||
"""Test Xiaomi vacuum fanspeeds."""
|
||||
entity_name = "test_vacuum_cleaner_2"
|
||||
entity_id = f"{DOMAIN}.{entity_name}"
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
DOMAIN,
|
||||
{
|
||||
DOMAIN: {
|
||||
CONF_PLATFORM: PLATFORM,
|
||||
CONF_HOST: "192.168.1.100",
|
||||
CONF_NAME: entity_name,
|
||||
CONF_TOKEN: "12345678901234567890123456789012",
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Initializing with host 192.168.1.100 (token 12345" in caplog.text
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.attributes.get(ATTR_FAN_SPEED) == "Silent"
|
||||
fanspeeds = state.attributes.get(ATTR_FAN_SPEED_LIST)
|
||||
for speed in ["Silent", "Standard", "Medium", "Turbo"]:
|
||||
assert speed in fanspeeds
|
||||
|
||||
# Set speed service:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{"entity_id": entity_id, "fan_speed": 60},
|
||||
blocking=True,
|
||||
)
|
||||
mock_mirobo_fanspeeds.assert_has_calls(
|
||||
[mock.call.set_fan_speed(60)], any_order=True
|
||||
)
|
||||
mock_mirobo_fanspeeds.assert_has_calls(STATUS_CALLS, any_order=True)
|
||||
mock_mirobo_fanspeeds.reset_mock()
|
||||
|
||||
fan_speed_dict = mock_mirobo_fanspeeds.fan_speed_presets()
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{"entity_id": entity_id, "fan_speed": "Medium"},
|
||||
blocking=True,
|
||||
)
|
||||
mock_mirobo_fanspeeds.assert_has_calls(
|
||||
[mock.call.set_fan_speed(fan_speed_dict["Medium"])], any_order=True
|
||||
)
|
||||
mock_mirobo_fanspeeds.assert_has_calls(STATUS_CALLS, any_order=True)
|
||||
mock_mirobo_fanspeeds.reset_mock()
|
||||
|
||||
assert "ERROR" not in caplog.text
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_FAN_SPEED,
|
||||
{"entity_id": entity_id, "fan_speed": "invent"},
|
||||
blocking=True,
|
||||
)
|
||||
assert "ERROR" in caplog.text
|
||||
|
|
Loading…
Add table
Reference in a new issue