Bump python-roborock to 0.36.0 (#103465)
This commit is contained in:
parent
a78ef60773
commit
44fe704f49
13 changed files with 77 additions and 43 deletions
|
@ -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
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
|
|
Loading…
Add table
Reference in a new issue