Skip to content

Commit a880ad2

Browse files
authored
Update Roborock entities to handle unavailable data (home-assistant#165618)
1 parent ea73f2d commit a880ad2

6 files changed

Lines changed: 49 additions & 31 deletions

File tree

homeassistant/components/roborock/binary_sensor.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,11 @@ async def async_setup_entry(
159159
)
160160
for coordinator in config_entry.runtime_data.v1
161161
for description in BINARY_SENSOR_DESCRIPTIONS
162-
if description.value_fn(coordinator.data) is not None
162+
# Note: Currently coordinator.data is always available on startup but won't be in the future
163+
if (
164+
coordinator.data is not None
165+
and description.value_fn(coordinator.data) is not None
166+
)
163167
]
164168
entities.extend(
165169
RoborockBinarySensorEntityA01(
@@ -193,9 +197,11 @@ def __init__(
193197
self.entity_description = description
194198

195199
@property
196-
def is_on(self) -> bool:
200+
def is_on(self) -> bool | None:
197201
"""Return the value reported by the sensor."""
198-
return bool(self.entity_description.value_fn(self.coordinator.data))
202+
if (data := self.coordinator.data) is not None:
203+
return bool(self.entity_description.value_fn(data))
204+
return None
199205

200206

201207
class RoborockBinarySensorEntityA01(RoborockCoordinatedEntityA01, BinarySensorEntity):

homeassistant/components/roborock/coordinator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def values(
8383
type RoborockConfigEntry = ConfigEntry[RoborockCoordinators]
8484

8585

86-
class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceState]):
86+
class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceState | None]):
8787
"""Class to manage fetching data from the API."""
8888

8989
config_entry: RoborockConfigEntry
@@ -229,7 +229,7 @@ async def _update_device_prop(self) -> None:
229229
)
230230
_LOGGER.debug("Updated device properties")
231231

232-
async def _async_update_data(self) -> DeviceState:
232+
async def _async_update_data(self) -> DeviceState | None:
233233
"""Update data via library."""
234234
await self._verify_api()
235235
try:

homeassistant/components/roborock/entity.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from typing import Any
44

55
from roborock.devices.traits.v1.command import CommandTrait
6-
from roborock.devices.traits.v1.status import StatusTrait
76
from roborock.exceptions import RoborockException
87
from roborock.roborock_typing import RoborockCommand
98

@@ -94,12 +93,6 @@ def __init__(
9493
CoordinatorEntity.__init__(self, coordinator=coordinator)
9594
self._attr_unique_id = unique_id
9695

97-
@property
98-
def _device_status(self) -> StatusTrait:
99-
"""Return the status of the device."""
100-
data = self.coordinator.data
101-
return data.status
102-
10396
async def send(
10497
self,
10598
command: RoborockCommand | str,

homeassistant/components/roborock/image.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from homeassistant.components.image import ImageEntity
1010
from homeassistant.config_entries import ConfigEntry
1111
from homeassistant.const import EntityCategory
12-
from homeassistant.core import HomeAssistant
12+
from homeassistant.core import HomeAssistant, callback
1313
from homeassistant.exceptions import HomeAssistantError
1414
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
1515

@@ -73,6 +73,7 @@ def __init__(
7373
self.map_flag = map_flag
7474
self.cached_map: bytes | None = None
7575
self._attr_entity_category = EntityCategory.DIAGNOSTIC
76+
self._attr_image_last_updated = None
7677

7778
async def async_added_to_hass(self) -> None:
7879
"""When entity is added to hass load any previously cached maps from disk."""
@@ -88,17 +89,17 @@ def _map_content(self) -> MapContent | None:
8889
return map_content
8990
return None
9091

92+
@callback
9193
def _handle_coordinator_update(self) -> None:
9294
"""Handle updated data from the coordinator.
9395
9496
If the coordinator has updated the map, we can update the image.
9597
"""
96-
if (map_content := self._map_content) is None:
98+
if self.coordinator.data is None or (map_content := self._map_content) is None:
9799
return
98100
if self.cached_map != map_content.image_content:
99101
self.cached_map = map_content.image_content
100102
self._attr_image_last_updated = self.coordinator.last_home_update
101-
102103
super()._handle_coordinator_update()
103104

104105
async def async_image(self) -> bytes | None:

homeassistant/components/roborock/sensor.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,11 @@ async def async_setup_entry(
427427
)
428428
for coordinator in coordinators.v1
429429
for description in SENSOR_DESCRIPTIONS
430-
if description.value_fn(coordinator.data) is not None
430+
# Note: Currently coordinator.data is always available on startup but won't be in the future
431+
if (
432+
coordinator.data is not None
433+
and description.value_fn(coordinator.data) is not None
434+
)
431435
]
432436
entities.extend(RoborockCurrentRoom(coordinator) for coordinator in coordinators.v1)
433437
entities.extend(
@@ -480,6 +484,8 @@ def __init__(
480484
@property
481485
def native_value(self) -> StateType | datetime.datetime:
482486
"""Return the value reported by the sensor."""
487+
if self.coordinator.data is None:
488+
return None
483489
return self.entity_description.value_fn(self.coordinator.data)
484490

485491

homeassistant/components/roborock/vacuum.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def __init__(
150150
coordinator.duid_slug,
151151
coordinator,
152152
)
153+
self._status_trait = coordinator.properties_api.status
153154
self._home_trait = coordinator.properties_api.home
154155
self._maps_trait = coordinator.properties_api.maps
155156

@@ -176,31 +177,37 @@ def _handle_coordinator_update(self) -> None:
176177
@property
177178
def fan_speed_list(self) -> list[str]:
178179
"""Get the list of available fan speeds."""
179-
return [mode.value for mode in self._device_status.fan_speed_options]
180+
if self.coordinator.data is None:
181+
return []
182+
return [mode.value for mode in self._status_trait.fan_speed_options]
180183

181184
@property
182185
def activity(self) -> VacuumActivity | None:
183186
"""Return the status of the vacuum cleaner."""
184-
assert self._device_status.state is not None
185-
return STATE_CODE_TO_STATE.get(self._device_status.state)
187+
if self.coordinator.data is None or self._status_trait.state is None:
188+
return None
189+
return STATE_CODE_TO_STATE.get(self._status_trait.state)
186190

187191
@property
188192
def fan_speed(self) -> str | None:
189193
"""Return the fan speed of the vacuum cleaner."""
190-
return self._device_status.fan_speed_name
194+
if self.coordinator.data is None:
195+
return None
196+
return self._status_trait.fan_speed_name
191197

192198
async def async_start(self) -> None:
193199
"""Start the vacuum."""
194-
if self._device_status.in_returning == 1:
195-
await self.send(RoborockCommand.APP_CHARGE)
196-
elif self._device_status.in_cleaning == 2:
197-
await self.send(RoborockCommand.RESUME_ZONED_CLEAN)
198-
elif self._device_status.in_cleaning == 3:
199-
await self.send(RoborockCommand.RESUME_SEGMENT_CLEAN)
200-
elif self._device_status.in_cleaning == 4:
201-
await self.send(RoborockCommand.APP_RESUME_BUILD_MAP)
202-
else:
203-
await self.send(RoborockCommand.APP_START)
200+
command = RoborockCommand.APP_START
201+
if self.coordinator.data is not None:
202+
if self._status_trait.in_returning == 1:
203+
command = RoborockCommand.APP_CHARGE
204+
elif self._status_trait.in_cleaning == 2:
205+
command = RoborockCommand.RESUME_ZONED_CLEAN
206+
elif self._status_trait.in_cleaning == 3:
207+
command = RoborockCommand.RESUME_SEGMENT_CLEAN
208+
elif self._status_trait.in_cleaning == 4:
209+
command = RoborockCommand.APP_RESUME_BUILD_MAP
210+
await self.send(command)
204211

205212
async def async_pause(self) -> None:
206213
"""Pause the vacuum."""
@@ -224,10 +231,15 @@ async def async_locate(self, **kwargs: Any) -> None:
224231

225232
async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
226233
"""Set vacuum fan speed."""
234+
if self.coordinator.data is None:
235+
raise HomeAssistantError(
236+
translation_domain=DOMAIN,
237+
translation_key="update_options_failed",
238+
)
227239
await self.send(
228240
RoborockCommand.SET_CUSTOM_MODE,
229241
[
230-
{v: k for k, v in self._device_status.fan_speed_mapping.items()}[
242+
{v: k for k, v in self._status_trait.fan_speed_mapping.items()}[
231243
fan_speed
232244
]
233245
],

0 commit comments

Comments
 (0)