Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/xiaomi_miio/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
20 changes: 13 additions & 7 deletions homeassistant/components/xiaomi_miio/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@
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"
Expand Down Expand Up @@ -246,6 +244,8 @@ def __init__(self, name, vacuum):
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):
Expand Down Expand Up @@ -281,14 +281,17 @@ def fan_speed(self):
"""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):
Expand Down Expand Up @@ -372,8 +375,8 @@ async def async_stop(self, **kwargs):

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)
Expand Down Expand Up @@ -453,6 +456,9 @@ def update(self):
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()
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1623,7 +1623,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
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,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
Expand Down
144 changes: 94 additions & 50 deletions tests/components/xiaomi_miio/test_vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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