From 0ce1117287f310cedeeef486366f704b0817ac4f Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 18 May 2023 23:55:39 -0400 Subject: [PATCH] Bump Roborock to 17.0 adding device specific support and bugfixes (#92547) * init commit * use official version release * remove options * moved first refresh to gather * add extra tests * remove model_sepcification * remove old mqtt test * bump to 13.4 * fix dndtimer * bump to 14.1 * add status back * bump to 17.0 * remove error as it is not used * addressing mr comments * making enum access use get() * add check for empty hass data --- homeassistant/components/roborock/__init__.py | 69 ++++++++++----- .../components/roborock/coordinator.py | 67 +++++---------- homeassistant/components/roborock/device.py | 36 ++++---- .../components/roborock/manifest.json | 2 +- homeassistant/components/roborock/models.py | 2 +- homeassistant/components/roborock/select.py | 56 ++++++------- .../components/roborock/strings.json | 3 +- homeassistant/components/roborock/vacuum.py | 83 ++++++++----------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/roborock/mock_data.py | 11 ++- tests/components/roborock/test_init.py | 23 ----- tests/components/roborock/test_vacuum.py | 9 +- 13 files changed, 161 insertions(+), 204 deletions(-) diff --git a/homeassistant/components/roborock/__init__.py b/homeassistant/components/roborock/__init__.py index 1ea5e4734bb..1a308f9dff9 100644 --- a/homeassistant/components/roborock/__init__.py +++ b/homeassistant/components/roborock/__init__.py @@ -7,8 +7,7 @@ import logging from roborock.api import RoborockApiClient from roborock.cloud_api import RoborockMqttClient -from roborock.containers import HomeDataDevice, RoborockDeviceInfo, UserData -from roborock.exceptions import RoborockException +from roborock.containers import DeviceData, HomeDataDevice, UserData from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_USERNAME @@ -32,39 +31,58 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.debug("Getting home data") home_data = await api_client.get_home_data(user_data) _LOGGER.debug("Got home data %s", home_data) - devices: list[HomeDataDevice] = home_data.devices + home_data.received_devices + device_map: dict[str, HomeDataDevice] = { + device.duid: device for device in home_data.devices + home_data.received_devices + } + product_info = {product.id: product for product in home_data.products} # Create a mqtt_client, which is needed to get the networking information of the device for local connection and in the future, get the map. - mqtt_client = RoborockMqttClient( - user_data, {device.duid: RoborockDeviceInfo(device) for device in devices} - ) + mqtt_clients = [ + RoborockMqttClient( + user_data, DeviceData(device, product_info[device.product_id].model) + ) + for device in device_map.values() + ] network_results = await asyncio.gather( - *(mqtt_client.get_networking(device.duid) for device in devices) + *(mqtt_client.get_networking() for mqtt_client in mqtt_clients) ) network_info = { device.duid: result - for device, result in zip(devices, network_results) + for device, result in zip(device_map.values(), network_results) if result is not None } - try: - await mqtt_client.async_disconnect() - except RoborockException as err: - _LOGGER.warning("Failed disconnecting from the mqtt server %s", err) + await asyncio.gather( + *(mqtt_client.async_disconnect() for mqtt_client in mqtt_clients), + return_exceptions=True, + ) if not network_info: raise ConfigEntryNotReady( "Could not get network information about your devices" ) - - product_info = {product.id: product for product in home_data.products} - coordinator = RoborockDataUpdateCoordinator( - hass, - devices, - network_info, - product_info, + coordinator_map: dict[str, RoborockDataUpdateCoordinator] = {} + for device_id, device in device_map.items(): + coordinator_map[device_id] = RoborockDataUpdateCoordinator( + hass, + device, + network_info[device_id], + product_info[device.product_id], + ) + # If one device update fails - we still want to set up other devices + await asyncio.gather( + *( + coordinator.async_config_entry_first_refresh() + for coordinator in coordinator_map.values() + ), + return_exceptions=True, ) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = { + device_id: coordinator + for device_id, coordinator in coordinator_map.items() + if coordinator.last_update_success + } # Only add coordinators that succeeded - await coordinator.async_config_entry_first_refresh() - - hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator + if not hass.data[DOMAIN][entry.entry_id]: + # Don't start if no coordinators succeeded. + raise ConfigEntryNotReady("There are no devices that can currently be reached.") await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -75,7 +93,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Handle removal of an entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) if unload_ok: - await hass.data[DOMAIN][entry.entry_id].release() + await asyncio.gather( + *( + coordinator.release() + for coordinator in hass.data[DOMAIN][entry.entry_id].values() + ) + ) hass.data[DOMAIN].pop(entry.entry_id) return unload_ok diff --git a/homeassistant/components/roborock/coordinator.py b/homeassistant/components/roborock/coordinator.py index 433b46d2899..acaa2bfa3f2 100644 --- a/homeassistant/components/roborock/coordinator.py +++ b/homeassistant/components/roborock/coordinator.py @@ -1,19 +1,13 @@ """Roborock Coordinator.""" from __future__ import annotations -import asyncio from datetime import timedelta import logging -from roborock.containers import ( - HomeDataDevice, - HomeDataProduct, - NetworkInfo, - RoborockLocalDeviceInfo, -) +from roborock.containers import DeviceData, HomeDataDevice, HomeDataProduct, NetworkInfo from roborock.exceptions import RoborockException from roborock.local_api import RoborockLocalClient -from roborock.typing import DeviceProp +from roborock.roborock_typing import DeviceProp from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -26,61 +20,44 @@ SCAN_INTERVAL = timedelta(seconds=30) _LOGGER = logging.getLogger(__name__) -class RoborockDataUpdateCoordinator(DataUpdateCoordinator[dict[str, DeviceProp]]): +class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]): """Class to manage fetching data from the API.""" def __init__( self, hass: HomeAssistant, - devices: list[HomeDataDevice], - devices_networking: dict[str, NetworkInfo], - product_info: dict[str, HomeDataProduct], + device: HomeDataDevice, + device_networking: NetworkInfo, + product_info: HomeDataProduct, ) -> None: """Initialize.""" super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL) - local_devices_info: dict[str, RoborockLocalDeviceInfo] = {} - hass_devices_info: dict[str, RoborockHassDeviceInfo] = {} - for device in devices: - if not (networking := devices_networking.get(device.duid)): - _LOGGER.warning("Device %s is offline and cannot be setup", device.duid) - continue - hass_devices_info[device.duid] = RoborockHassDeviceInfo( - device, - networking, - product_info[device.product_id], - DeviceProp(), - ) - local_devices_info[device.duid] = RoborockLocalDeviceInfo( - device, networking - ) - self.api = RoborockLocalClient(local_devices_info) - self.devices_info = hass_devices_info + self.device_info = RoborockHassDeviceInfo( + device, + device_networking, + product_info, + DeviceProp(), + ) + device_info = DeviceData(device, product_info.model, device_networking.ip) + self.api = RoborockLocalClient(device_info) async def release(self) -> None: """Disconnect from API.""" await self.api.async_disconnect() - async def _update_device_prop(self, device_info: RoborockHassDeviceInfo) -> None: + async def _update_device_prop(self) -> None: """Update device properties.""" - device_prop = await self.api.get_prop(device_info.device.duid) + device_prop = await self.api.get_prop() if device_prop: - if device_info.props: - device_info.props.update(device_prop) + if self.device_info.props: + self.device_info.props.update(device_prop) else: - device_info.props = device_prop + self.device_info.props = device_prop - async def _async_update_data(self) -> dict[str, DeviceProp]: + async def _async_update_data(self) -> DeviceProp: """Update data via library.""" try: - await asyncio.gather( - *( - self._update_device_prop(device_info) - for device_info in self.devices_info.values() - ) - ) + await self._update_device_prop() except RoborockException as ex: raise UpdateFailed(ex) from ex - return { - device_id: device_info.props - for device_id, device_info in self.devices_info.items() - } + return self.device_info.props diff --git a/homeassistant/components/roborock/device.py b/homeassistant/components/roborock/device.py index e544147e9b8..39a9524226d 100644 --- a/homeassistant/components/roborock/device.py +++ b/homeassistant/components/roborock/device.py @@ -3,14 +3,15 @@ from typing import Any from roborock.containers import Status -from roborock.typing import RoborockCommand +from roborock.exceptions import RoborockException +from roborock.roborock_typing import RoborockCommand +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import RoborockDataUpdateCoordinator from .const import DOMAIN -from .models import RoborockHassDeviceInfo class RoborockCoordinatedEntity(CoordinatorEntity[RoborockDataUpdateCoordinator]): @@ -21,46 +22,43 @@ class RoborockCoordinatedEntity(CoordinatorEntity[RoborockDataUpdateCoordinator] def __init__( self, unique_id: str, - device_info: RoborockHassDeviceInfo, coordinator: RoborockDataUpdateCoordinator, ) -> None: """Initialize the coordinated Roborock Device.""" super().__init__(coordinator) self._attr_unique_id = unique_id - self._device_name = device_info.device.name - self._device_id = device_info.device.duid - self._device_model = device_info.product.model - self._fw_version = device_info.device.fv @property def _device_status(self) -> Status: """Return the status of the device.""" data = self.coordinator.data if data: - device_data = data.get(self._device_id) - if device_data: - status = device_data.status - if status: - return status + status = data.status + if status: + return status return Status({}) @property def device_info(self) -> DeviceInfo: """Return the device info.""" return DeviceInfo( - name=self._device_name, - identifiers={(DOMAIN, self._device_id)}, + name=self.coordinator.device_info.device.name, + identifiers={(DOMAIN, self.coordinator.device_info.device.duid)}, manufacturer="Roborock", - model=self._device_model, - sw_version=self._fw_version, + model=self.coordinator.device_info.product.model, + sw_version=self.coordinator.device_info.device.fv, ) async def send( self, command: RoborockCommand, params: dict[str, Any] | list[Any] | None = None ) -> dict: """Send a command to a vacuum cleaner.""" - response = await self.coordinator.api.send_command( - self._device_id, command, params - ) + try: + response = await self.coordinator.api.send_command(command, params) + except RoborockException as err: + raise HomeAssistantError( + f"Error while calling {command.name} with {params}" + ) from err + await self.coordinator.async_request_refresh() return response diff --git a/homeassistant/components/roborock/manifest.json b/homeassistant/components/roborock/manifest.json index 7cb686f1851..44a4cba89c9 100644 --- a/homeassistant/components/roborock/manifest.json +++ b/homeassistant/components/roborock/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/roborock", "iot_class": "local_polling", "loggers": ["roborock"], - "requirements": ["python-roborock==0.8.3"] + "requirements": ["python-roborock==0.17.0"] } diff --git a/homeassistant/components/roborock/models.py b/homeassistant/components/roborock/models.py index 0377cebd425..a30c84ce1da 100644 --- a/homeassistant/components/roborock/models.py +++ b/homeassistant/components/roborock/models.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from roborock.containers import HomeDataDevice, HomeDataProduct, NetworkInfo -from roborock.typing import DeviceProp +from roborock.roborock_typing import DeviceProp @dataclass diff --git a/homeassistant/components/roborock/select.py b/homeassistant/components/roborock/select.py index 646c4904854..d27888a1779 100644 --- a/homeassistant/components/roborock/select.py +++ b/homeassistant/components/roborock/select.py @@ -2,31 +2,32 @@ from collections.abc import Callable from dataclasses import dataclass -from roborock.code_mappings import RoborockMopIntensityCode, RoborockMopModeCode from roborock.containers import Status -from roborock.exceptions import RoborockException -from roborock.typing import RoborockCommand +from roborock.roborock_typing import RoborockCommand from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.util import slugify from .const import DOMAIN from .coordinator import RoborockDataUpdateCoordinator from .device import RoborockCoordinatedEntity -from .models import RoborockHassDeviceInfo @dataclass class RoborockSelectDescriptionMixin: """Define an entity description mixin for select entities.""" + # The command that the select entity will send to the api. api_command: RoborockCommand + # Gets the current value of the select entity. value_fn: Callable[[Status], str] - options_lambda: Callable[[str], list[int]] + # Gets all options of the select entity. + options_lambda: Callable[[Status], list[str]] + # Takes the value from the select entiy and converts it for the api. + parameter_lambda: Callable[[str, Status], list[int]] @dataclass @@ -40,22 +41,20 @@ SELECT_DESCRIPTIONS: list[RoborockSelectDescription] = [ RoborockSelectDescription( key="water_box_mode", translation_key="mop_intensity", - options=RoborockMopIntensityCode.values(), api_command=RoborockCommand.SET_WATER_BOX_CUSTOM_MODE, - value_fn=lambda data: data.water_box_mode, - options_lambda=lambda data: [ - k for k, v in RoborockMopIntensityCode.items() if v == data - ], + value_fn=lambda data: data.water_box_mode.name, + options_lambda=lambda data: data.water_box_mode.keys() + if data.water_box_mode + else None, + parameter_lambda=lambda key, status: [status.water_box_mode.as_dict().get(key)], ), RoborockSelectDescription( key="mop_mode", translation_key="mop_mode", - options=RoborockMopModeCode.values(), api_command=RoborockCommand.SET_MOP_MODE, - value_fn=lambda data: data.mop_mode, - options_lambda=lambda data: [ - k for k, v in RoborockMopModeCode.items() if v == data - ], + value_fn=lambda data: data.mop_mode.name, + options_lambda=lambda data: data.mop_mode.keys() if data.mop_mode else None, + parameter_lambda=lambda key, status: [status.mop_mode.as_dict().get(key)], ), ] @@ -67,18 +66,18 @@ async def async_setup_entry( ) -> None: """Set up Roborock select platform.""" - coordinator: RoborockDataUpdateCoordinator = hass.data[DOMAIN][ + coordinators: dict[str, RoborockDataUpdateCoordinator] = hass.data[DOMAIN][ config_entry.entry_id ] async_add_entities( RoborockSelectEntity( f"{description.key}_{slugify(device_id)}", - device_info, coordinator, description, ) - for device_id, device_info in coordinator.devices_info.items() + for device_id, coordinator in coordinators.items() for description in SELECT_DESCRIPTIONS + if description.options_lambda(coordinator.device_info.props.status) is not None ) @@ -90,25 +89,20 @@ class RoborockSelectEntity(RoborockCoordinatedEntity, SelectEntity): def __init__( self, unique_id: str, - device_info: RoborockHassDeviceInfo, coordinator: RoborockDataUpdateCoordinator, entity_description: RoborockSelectDescription, ) -> None: """Create a select entity.""" self.entity_description = entity_description - super().__init__(unique_id, device_info, coordinator) + super().__init__(unique_id, coordinator) + self._attr_options = self.entity_description.options_lambda(self._device_status) async def async_select_option(self, option: str) -> None: - """Set the mop intensity.""" - try: - await self.send( - self.entity_description.api_command, - self.entity_description.options_lambda(option), - ) - except RoborockException as err: - raise HomeAssistantError( - f"Error while setting {self.entity_description.key} to {option}" - ) from err + """Set the option.""" + await self.send( + self.entity_description.api_command, + self.entity_description.parameter_lambda(option, self._device_status), + ) @property def current_option(self) -> str | None: diff --git a/homeassistant/components/roborock/strings.json b/homeassistant/components/roborock/strings.json index 6bd19787d20..64c9d268e56 100644 --- a/homeassistant/components/roborock/strings.json +++ b/homeassistant/components/roborock/strings.json @@ -34,7 +34,8 @@ "standard": "Standard", "deep": "Deep", "deep_plus": "Deep+", - "custom": "Custom" + "custom": "Custom", + "fast": "Fast" } }, "mop_intensity": { diff --git a/homeassistant/components/roborock/vacuum.py b/homeassistant/components/roborock/vacuum.py index 4306afb25e4..666b8488d80 100644 --- a/homeassistant/components/roborock/vacuum.py +++ b/homeassistant/components/roborock/vacuum.py @@ -1,8 +1,8 @@ """Support for Roborock vacuum class.""" from typing import Any -from roborock.code_mappings import RoborockFanPowerCode, RoborockStateCode -from roborock.typing import RoborockCommand +from roborock.code_mappings import RoborockStateCode +from roborock.roborock_typing import RoborockCommand from homeassistant.components.vacuum import ( STATE_CLEANING, @@ -22,51 +22,46 @@ from homeassistant.util import slugify from .const import DOMAIN from .coordinator import RoborockDataUpdateCoordinator from .device import RoborockCoordinatedEntity -from .models import RoborockHassDeviceInfo STATE_CODE_TO_STATE = { - RoborockStateCode["1"]: STATE_IDLE, # "Starting" - RoborockStateCode["2"]: STATE_IDLE, # "Charger disconnected" - RoborockStateCode["3"]: STATE_IDLE, # "Idle" - RoborockStateCode["4"]: STATE_CLEANING, # "Remote control active" - RoborockStateCode["5"]: STATE_CLEANING, # "Cleaning" - RoborockStateCode["6"]: STATE_RETURNING, # "Returning home" - RoborockStateCode["7"]: STATE_CLEANING, # "Manual mode" - RoborockStateCode["8"]: STATE_DOCKED, # "Charging" - RoborockStateCode["9"]: STATE_ERROR, # "Charging problem" - RoborockStateCode["10"]: STATE_PAUSED, # "Paused" - RoborockStateCode["11"]: STATE_CLEANING, # "Spot cleaning" - RoborockStateCode["12"]: STATE_ERROR, # "Error" - RoborockStateCode["13"]: STATE_IDLE, # "Shutting down" - RoborockStateCode["14"]: STATE_DOCKED, # "Updating" - RoborockStateCode["15"]: STATE_RETURNING, # "Docking" - RoborockStateCode["16"]: STATE_CLEANING, # "Going to target" - RoborockStateCode["17"]: STATE_CLEANING, # "Zoned cleaning" - RoborockStateCode["18"]: STATE_CLEANING, # "Segment cleaning" - RoborockStateCode["22"]: STATE_DOCKED, # "Emptying the bin" on s7+ - RoborockStateCode["23"]: STATE_DOCKED, # "Washing the mop" on s7maxV - RoborockStateCode["26"]: STATE_RETURNING, # "Going to wash the mop" on s7maxV - RoborockStateCode["100"]: STATE_DOCKED, # "Charging complete" - RoborockStateCode["101"]: STATE_ERROR, # "Device offline" + RoborockStateCode.starting: STATE_IDLE, # "Starting" + RoborockStateCode.charger_disconnected: STATE_IDLE, # "Charger disconnected" + RoborockStateCode.idle: STATE_IDLE, # "Idle" + RoborockStateCode.remote_control_active: STATE_CLEANING, # "Remote control active" + RoborockStateCode.cleaning: STATE_CLEANING, # "Cleaning" + RoborockStateCode.returning_home: STATE_RETURNING, # "Returning home" + RoborockStateCode.manual_mode: STATE_CLEANING, # "Manual mode" + RoborockStateCode.charging: STATE_DOCKED, # "Charging" + RoborockStateCode.charging_problem: STATE_ERROR, # "Charging problem" + RoborockStateCode.paused: STATE_PAUSED, # "Paused" + RoborockStateCode.spot_cleaning: STATE_CLEANING, # "Spot cleaning" + RoborockStateCode.error: STATE_ERROR, # "Error" + RoborockStateCode.shutting_down: STATE_IDLE, # "Shutting down" + RoborockStateCode.updating: STATE_DOCKED, # "Updating" + RoborockStateCode.docking: STATE_RETURNING, # "Docking" + RoborockStateCode.going_to_target: STATE_CLEANING, # "Going to target" + RoborockStateCode.zoned_cleaning: STATE_CLEANING, # "Zoned cleaning" + RoborockStateCode.segment_cleaning: STATE_CLEANING, # "Segment cleaning" + RoborockStateCode.emptying_the_bin: STATE_DOCKED, # "Emptying the bin" on s7+ + RoborockStateCode.washing_the_mop: STATE_DOCKED, # "Washing the mop" on s7maxV + RoborockStateCode.going_to_wash_the_mop: STATE_RETURNING, # "Going to wash the mop" on s7maxV + RoborockStateCode.charging_complete: STATE_DOCKED, # "Charging complete" + RoborockStateCode.device_offline: STATE_ERROR, # "Device offline" } -ATTR_STATUS = "status" -ATTR_ERROR = "error" - - async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Set up the Roborock sensor.""" - coordinator: RoborockDataUpdateCoordinator = hass.data[DOMAIN][ + coordinators: dict[str, RoborockDataUpdateCoordinator] = hass.data[DOMAIN][ config_entry.entry_id ] async_add_entities( - RoborockVacuum(slugify(device_id), device_info, coordinator) - for device_id, device_info in coordinator.devices_info.items() + RoborockVacuum(slugify(device_id), coordinator) + for device_id, coordinator in coordinators.items() ) @@ -87,28 +82,22 @@ class RoborockVacuum(RoborockCoordinatedEntity, StateVacuumEntity): | VacuumEntityFeature.STATE | VacuumEntityFeature.START ) - _attr_fan_speed_list = RoborockFanPowerCode.values() def __init__( self, unique_id: str, - device: RoborockHassDeviceInfo, coordinator: RoborockDataUpdateCoordinator, ) -> None: """Initialize a vacuum.""" StateVacuumEntity.__init__(self) - RoborockCoordinatedEntity.__init__(self, unique_id, device, coordinator) + RoborockCoordinatedEntity.__init__(self, unique_id, coordinator) + self._attr_fan_speed_list = self._device_status.fan_power.keys() @property def state(self) -> str | None: """Return the status of the vacuum cleaner.""" return STATE_CODE_TO_STATE.get(self._device_status.state) - @property - def status(self) -> str | None: - """Return the status of the vacuum cleaner.""" - return self._device_status.status - @property def battery_level(self) -> int | None: """Return the battery level of the vacuum cleaner.""" @@ -117,12 +106,12 @@ class RoborockVacuum(RoborockCoordinatedEntity, StateVacuumEntity): @property def fan_speed(self) -> str | None: """Return the fan speed of the vacuum cleaner.""" - return self._device_status.fan_power + return self._device_status.fan_power.name @property - def error(self) -> str | None: - """Get the error str if an error code exists.""" - return self._device_status.error + def status(self) -> str | None: + """Return the status of the vacuum cleaner.""" + return self._device_status.state.name async def async_start(self) -> None: """Start the vacuum.""" @@ -152,11 +141,11 @@ class RoborockVacuum(RoborockCoordinatedEntity, StateVacuumEntity): """Set vacuum fan speed.""" await self.send( RoborockCommand.SET_CUSTOM_MODE, - [k for k, v in RoborockFanPowerCode.items() if v == fan_speed], + [self._device_status.fan_power.as_dict().get(fan_speed)], ) await self.coordinator.async_request_refresh() - async def async_start_pause(self): + async def async_start_pause(self) -> None: """Start, pause or resume the cleaning task.""" if self.state == STATE_CLEANING: await self.async_pause() diff --git a/requirements_all.txt b/requirements_all.txt index 93a22f3a149..1edeab6218e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2111,7 +2111,7 @@ python-qbittorrent==0.4.2 python-ripple-api==0.0.3 # homeassistant.components.roborock -python-roborock==0.8.3 +python-roborock==0.17.0 # homeassistant.components.smarttub python-smarttub==0.0.33 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bcedff42ac1..f2e3928e664 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1531,7 +1531,7 @@ python-picnic-api==1.1.0 python-qbittorrent==0.4.2 # homeassistant.components.roborock -python-roborock==0.8.3 +python-roborock==0.17.0 # homeassistant.components.smarttub python-smarttub==0.0.33 diff --git a/tests/components/roborock/mock_data.py b/tests/components/roborock/mock_data.py index 55eb8086842..15e69cee9d9 100644 --- a/tests/components/roborock/mock_data.py +++ b/tests/components/roborock/mock_data.py @@ -5,13 +5,13 @@ from roborock.containers import ( CleanRecord, CleanSummary, Consumable, - DNDTimer, + DnDTimer, HomeData, NetworkInfo, - Status, + S7Status, UserData, ) -from roborock.typing import DeviceProp +from roborock.roborock_typing import DeviceProp # All data is based on a U.S. customer with a Roborock S7 MaxV Ultra USER_EMAIL = "user@domain.com" @@ -311,7 +311,7 @@ CONSUMABLE = Consumable.from_dict( } ) -DND_TIMER = DNDTimer.from_dict( +DND_TIMER = DnDTimer.from_dict( { "start_hour": 22, "start_minute": 0, @@ -321,7 +321,7 @@ DND_TIMER = DNDTimer.from_dict( } ) -STATUS = Status.from_dict( +STATUS = S7Status.from_dict( { "msg_ver": 2, "msg_seq": 458, @@ -367,7 +367,6 @@ STATUS = Status.from_dict( "unsave_map_flag": 0, } ) - PROP = DeviceProp(STATUS, DND_TIMER, CLEAN_SUMMARY, CONSUMABLE, CLEAN_RECORD) NETWORK_INFO = NetworkInfo( diff --git a/tests/components/roborock/test_init.py b/tests/components/roborock/test_init.py index 18d9ee1bafe..05bf0848475 100644 --- a/tests/components/roborock/test_init.py +++ b/tests/components/roborock/test_init.py @@ -1,8 +1,6 @@ """Test for Roborock init.""" from unittest.mock import patch -from roborock.exceptions import RoborockTimeout - from homeassistant.components.roborock.const import DOMAIN from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant @@ -10,7 +8,6 @@ from homeassistant.helpers.update_coordinator import UpdateFailed from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry -from tests.components.roborock.mock_data import HOME_DATA, NETWORK_INFO async def test_unload_entry( @@ -41,23 +38,3 @@ async def test_config_entry_not_ready( ): await async_setup_component(hass, DOMAIN, {}) assert mock_roborock_entry.state is ConfigEntryState.SETUP_RETRY - - -async def test_continue_setup_mqtt_disconnect_fail( - hass: HomeAssistant, mock_roborock_entry: MockConfigEntry -): - """Test that if disconnect fails, we still continue setting up.""" - with patch( - "homeassistant.components.roborock.RoborockApiClient.get_home_data", - return_value=HOME_DATA, - ), patch( - "homeassistant.components.roborock.RoborockMqttClient.get_networking", - return_value=NETWORK_INFO, - ), patch( - "homeassistant.components.roborock.RoborockMqttClient.async_disconnect", - side_effect=RoborockTimeout(), - ), patch( - "homeassistant.components.roborock.RoborockDataUpdateCoordinator.async_config_entry_first_refresh" - ): - await async_setup_component(hass, DOMAIN, {}) - assert mock_roborock_entry.state is ConfigEntryState.LOADED diff --git a/tests/components/roborock/test_vacuum.py b/tests/components/roborock/test_vacuum.py index f6cc5e81d1b..80fbd4092c0 100644 --- a/tests/components/roborock/test_vacuum.py +++ b/tests/components/roborock/test_vacuum.py @@ -5,7 +5,7 @@ from typing import Any from unittest.mock import patch import pytest -from roborock.typing import RoborockCommand +from roborock.roborock_typing import RoborockCommand from homeassistant.components.vacuum import ( SERVICE_CLEAN_SPOT, @@ -50,7 +50,7 @@ async def test_registry_entries( ( SERVICE_SET_FAN_SPEED, RoborockCommand.SET_CUSTOM_MODE, - {"fan_speed": "silent"}, + {"fan_speed": "quiet"}, [101], ), ( @@ -86,6 +86,5 @@ async def test_commands( blocking=True, ) assert mock_send_command.call_count == 1 - assert mock_send_command.call_args[0][0] == DEVICE_ID - assert mock_send_command.call_args[0][1] == command - assert mock_send_command.call_args[0][2] == called_params + assert mock_send_command.call_args[0][0] == command + assert mock_send_command.call_args[0][1] == called_params