Bump python-roborock to 0.36.0 (#103465)

This commit is contained in:
Luke Lashley 2023-11-08 06:48:05 -05:00 committed by GitHub
parent a78ef60773
commit 44fe704f49
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 77 additions and 43 deletions

View file

@ -26,7 +26,7 @@ from .device import RoborockCoordinatedEntity
class RoborockBinarySensorDescriptionMixin:
"""A class that describes binary sensor entities."""
value_fn: Callable[[DeviceProp], bool]
value_fn: Callable[[DeviceProp], bool | int | None]
@dataclass

View file

@ -33,7 +33,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
device: HomeDataDevice,
device_networking: NetworkInfo,
product_info: HomeDataProduct,
cloud_api: RoborockMqttClient | None = None,
cloud_api: RoborockMqttClient,
) -> None:
"""Initialize."""
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)
@ -44,7 +44,9 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
DeviceProp(),
)
device_data = DeviceData(device, product_info.model, device_networking.ip)
self.api = RoborockLocalClient(device_data)
self.api: RoborockLocalClient | RoborockMqttClient = RoborockLocalClient(
device_data
)
self.cloud_api = cloud_api
self.device_info = DeviceInfo(
name=self.roborock_device_info.device.name,
@ -59,18 +61,18 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
async def verify_api(self) -> None:
"""Verify that the api is reachable. If it is not, switch clients."""
try:
await self.api.ping()
except RoborockException:
if isinstance(self.api, RoborockLocalClient):
if isinstance(self.api, RoborockLocalClient):
try:
await self.api.ping()
except RoborockException:
_LOGGER.warning(
"Using the cloud API for device %s. This is not recommended as it can lead to rate limiting. We recommend making your vacuum accessible by your Home Assistant instance",
self.roborock_device_info.device.duid,
)
# We use the cloud api if the local api fails to connect.
self.api = self.cloud_api
# Right now this should never be called if the cloud api is the primary api,
# but in the future if it is, a new else should be added.
# Right now this should never be called if the cloud api is the primary api,
# but in the future if it is, a new else should be added.
async def release(self) -> None:
"""Disconnect from API."""

View file

@ -36,7 +36,7 @@ class RoborockEntity(Entity):
def get_cache(self, attribute: CacheableAttribute) -> AttributeCache:
"""Get an item from the api cache."""
return self._api.cache.get(attribute)
return self._api.cache[attribute]
async def send(
self,
@ -45,7 +45,7 @@ class RoborockEntity(Entity):
) -> dict:
"""Send a command to a vacuum cleaner."""
try:
response = await self._api.send_command(command, params)
response: dict = await self._api.send_command(command, params)
except RoborockException as err:
raise HomeAssistantError(
f"Error while calling {command.name if isinstance(command, RoborockCommand) else command} with {params}"
@ -80,15 +80,11 @@ class RoborockCoordinatedEntity(
def _device_status(self) -> Status:
"""Return the status of the device."""
data = self.coordinator.data
if data:
status = data.status
if status:
return status
return Status({})
return data.status
async def send(
self,
command: RoborockCommand,
command: RoborockCommand | str,
params: dict[str, Any] | list[Any] | int | None = None,
) -> dict:
"""Overloads normal send command but refreshes coordinator."""

View file

@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/roborock",
"iot_class": "local_polling",
"loggers": ["roborock"],
"requirements": ["python-roborock==0.35.0"]
"requirements": ["python-roborock==0.36.0"]
}

View file

@ -74,7 +74,7 @@ async def async_setup_entry(
# We need to check if this function is supported by the device.
results = await asyncio.gather(
*(
coordinator.api.cache.get(description.cache_key).async_value()
coordinator.api.get_from_cache(description.cache_key)
for coordinator, description in possible_entities
),
return_exceptions=True,

View file

@ -24,9 +24,9 @@ class RoborockSelectDescriptionMixin:
# 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]
value_fn: Callable[[Status], str | None]
# Gets all options of the select entity.
options_lambda: Callable[[Status], list[str]]
options_lambda: Callable[[Status], list[str] | None]
# Takes the value from the select entiy and converts it for the api.
parameter_lambda: Callable[[str, Status], list[int]]
@ -43,21 +43,23 @@ SELECT_DESCRIPTIONS: list[RoborockSelectDescription] = [
key="water_box_mode",
translation_key="mop_intensity",
api_command=RoborockCommand.SET_WATER_BOX_CUSTOM_MODE,
value_fn=lambda data: data.water_box_mode.name,
value_fn=lambda data: data.water_box_mode_name,
entity_category=EntityCategory.CONFIG,
options_lambda=lambda data: data.water_box_mode.keys()
if data.water_box_mode
if data.water_box_mode is not None
else None,
parameter_lambda=lambda key, status: [status.water_box_mode.as_dict().get(key)],
parameter_lambda=lambda key, status: [status.get_mop_intensity_code(key)],
),
RoborockSelectDescription(
key="mop_mode",
translation_key="mop_mode",
api_command=RoborockCommand.SET_MOP_MODE,
value_fn=lambda data: data.mop_mode.name,
value_fn=lambda data: data.mop_mode_name,
entity_category=EntityCategory.CONFIG,
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)],
options_lambda=lambda data: data.mop_mode.keys()
if data.mop_mode is not None
else None,
parameter_lambda=lambda key, status: [status.get_mop_mode_code(key)],
),
]
@ -74,13 +76,15 @@ async def async_setup_entry(
]
async_add_entities(
RoborockSelectEntity(
f"{description.key}_{slugify(device_id)}",
coordinator,
description,
f"{description.key}_{slugify(device_id)}", coordinator, description, options
)
for device_id, coordinator in coordinators.items()
for description in SELECT_DESCRIPTIONS
if description.options_lambda(coordinator.roborock_device_info.props.status)
if (
options := description.options_lambda(
coordinator.roborock_device_info.props.status
)
)
is not None
)
@ -95,11 +99,12 @@ class RoborockSelectEntity(RoborockCoordinatedEntity, SelectEntity):
unique_id: str,
coordinator: RoborockDataUpdateCoordinator,
entity_description: RoborockSelectDescription,
options: list[str],
) -> None:
"""Create a select entity."""
self.entity_description = entity_description
super().__init__(unique_id, coordinator)
self._attr_options = self.entity_description.options_lambda(self._device_status)
self._attr_options = options
async def async_select_option(self, option: str) -> None:
"""Set the option."""

View file

@ -117,7 +117,7 @@ SENSOR_DESCRIPTIONS = [
icon="mdi:information-outline",
device_class=SensorDeviceClass.ENUM,
translation_key="status",
value_fn=lambda data: data.status.state.name,
value_fn=lambda data: data.status.state_name,
entity_category=EntityCategory.DIAGNOSTIC,
options=RoborockStateCode.keys(),
),
@ -142,7 +142,7 @@ SENSOR_DESCRIPTIONS = [
icon="mdi:alert-circle",
translation_key="vacuum_error",
device_class=SensorDeviceClass.ENUM,
value_fn=lambda data: data.status.error_code.name,
value_fn=lambda data: data.status.error_code_name,
entity_category=EntityCategory.DIAGNOSTIC,
options=RoborockErrorCode.keys(),
),
@ -157,7 +157,9 @@ SENSOR_DESCRIPTIONS = [
key="last_clean_start",
translation_key="last_clean_start",
icon="mdi:clock-time-twelve",
value_fn=lambda data: data.last_clean_record.begin_datetime,
value_fn=lambda data: data.last_clean_record.begin_datetime
if data.last_clean_record is not None
else None,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.TIMESTAMP,
),
@ -165,7 +167,9 @@ SENSOR_DESCRIPTIONS = [
key="last_clean_end",
translation_key="last_clean_end",
icon="mdi:clock-time-twelve",
value_fn=lambda data: data.last_clean_record.end_datetime,
value_fn=lambda data: data.last_clean_record.end_datetime
if data.last_clean_record is not None
else None,
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.TIMESTAMP,
),

View file

@ -125,7 +125,7 @@ async def async_setup_entry(
# We need to check if this function is supported by the device.
results = await asyncio.gather(
*(
coordinator.api.cache.get(description.cache_key).async_value()
coordinator.api.get_from_cache(description.cache_key)
for coordinator, description in possible_entities
),
return_exceptions=True,

View file

@ -139,7 +139,7 @@ async def async_setup_entry(
# We need to check if this function is supported by the device.
results = await asyncio.gather(
*(
coordinator.api.cache.get(description.cache_key).async_value()
coordinator.api.get_from_cache(description.cache_key)
for coordinator, description in possible_entities
),
return_exceptions=True,

View file

@ -93,11 +93,12 @@ class RoborockVacuum(RoborockCoordinatedEntity, StateVacuumEntity):
"""Initialize a vacuum."""
StateVacuumEntity.__init__(self)
RoborockCoordinatedEntity.__init__(self, unique_id, coordinator)
self._attr_fan_speed_list = self._device_status.fan_power.keys()
self._attr_fan_speed_list = self._device_status.fan_power_options
@property
def state(self) -> str | None:
"""Return the status of the vacuum cleaner."""
assert self._device_status.state is not None
return STATE_CODE_TO_STATE.get(self._device_status.state)
@property
@ -108,7 +109,7 @@ 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.name
return self._device_status.fan_power_name
async def async_start(self) -> None:
"""Start the vacuum."""
@ -138,7 +139,7 @@ class RoborockVacuum(RoborockCoordinatedEntity, StateVacuumEntity):
"""Set vacuum fan speed."""
await self.send(
RoborockCommand.SET_CUSTOM_MODE,
[self._device_status.fan_power.as_dict().get(fan_speed)],
[self._device_status.get_fan_speed_code(fan_speed)],
)
async def async_start_pause(self) -> None:

View file

@ -2184,7 +2184,7 @@ python-qbittorrent==0.4.3
python-ripple-api==0.0.3
# homeassistant.components.roborock
python-roborock==0.35.0
python-roborock==0.36.0
# homeassistant.components.smarttub
python-smarttub==0.0.35

View file

@ -1628,7 +1628,7 @@ python-picnic-api==1.1.0
python-qbittorrent==0.4.3
# homeassistant.components.roborock
python-roborock==0.35.0
python-roborock==0.36.0
# homeassistant.components.smarttub
python-smarttub==0.0.35

View file

@ -260,7 +260,17 @@
'dockType': 3,
'dustCollectionStatus': 0,
'errorCode': 0,
'errorCodeName': 'none',
'fanPower': 102,
'fanPowerName': 'balanced',
'fanPowerOptions': list([
'off',
'quiet',
'balanced',
'turbo',
'max',
'custom',
]),
'homeSecEnablePassword': 0,
'homeSecStatus': 0,
'inCleaning': 0,
@ -274,10 +284,12 @@
'mapStatus': 3,
'mopForbiddenEnable': 1,
'mopMode': 300,
'mopModeName': 'standard',
'msgSeq': 458,
'msgVer': 2,
'squareMeterCleanArea': 21.0,
'state': 8,
'stateName': 'charging',
'switchMapMode': 0,
'unsaveMapFlag': 0,
'unsaveMapReason': 0,
@ -285,6 +297,7 @@
'washReady': 0,
'waterBoxCarriageStatus': 1,
'waterBoxMode': 203,
'waterBoxModeName': 'intense',
'waterBoxStatus': 1,
'waterShortageStatus': 0,
}),
@ -521,7 +534,17 @@
'dockType': 3,
'dustCollectionStatus': 0,
'errorCode': 0,
'errorCodeName': 'none',
'fanPower': 102,
'fanPowerName': 'balanced',
'fanPowerOptions': list([
'off',
'quiet',
'balanced',
'turbo',
'max',
'custom',
]),
'homeSecEnablePassword': 0,
'homeSecStatus': 0,
'inCleaning': 0,
@ -535,10 +558,12 @@
'mapStatus': 3,
'mopForbiddenEnable': 1,
'mopMode': 300,
'mopModeName': 'standard',
'msgSeq': 458,
'msgVer': 2,
'squareMeterCleanArea': 21.0,
'state': 8,
'stateName': 'charging',
'switchMapMode': 0,
'unsaveMapFlag': 0,
'unsaveMapReason': 0,
@ -546,6 +571,7 @@
'washReady': 0,
'waterBoxCarriageStatus': 1,
'waterBoxMode': 203,
'waterBoxModeName': 'intense',
'waterBoxStatus': 1,
'waterShortageStatus': 0,
}),