mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add Roborock entity with the name of the current room (#140895)
* Add current room entity * Update homeassistant/components/roborock/models.py Co-authored-by: Allen Porter <allen.porter@gmail.com> * Update homeassistant/components/roborock/models.py Co-authored-by: Allen Porter <allen.porter@gmail.com> * use current_room property * remove select changes --------- Co-authored-by: Allen Porter <allen.porter@gmail.com>
This commit is contained in:
parent
c41d5f2577
commit
254622878a
@ -29,6 +29,7 @@ from roborock.web_api import RoborockApiClient
|
||||
from vacuum_map_parser_base.config.color import ColorsPalette
|
||||
from vacuum_map_parser_base.config.image_config import ImageConfig
|
||||
from vacuum_map_parser_base.config.size import Sizes
|
||||
from vacuum_map_parser_base.map_data import MapData
|
||||
from vacuum_map_parser_roborock.map_data_parser import RoborockMapDataParser
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -168,18 +169,20 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
|
||||
sw_version=self.roborock_device_info.device.fv,
|
||||
)
|
||||
|
||||
def parse_image(self, map_bytes: bytes) -> bytes | None:
|
||||
"""Parse map_bytes and store it as image bytes."""
|
||||
def parse_map_data_v1(
|
||||
self, map_bytes: bytes
|
||||
) -> tuple[bytes | None, MapData | None]:
|
||||
"""Parse map_bytes and return MapData and the image."""
|
||||
try:
|
||||
parsed_map = self.map_parser.parse(map_bytes)
|
||||
except (IndexError, ValueError) as err:
|
||||
_LOGGER.debug("Exception when parsing map contents: %s", err)
|
||||
return None
|
||||
return None, None
|
||||
if parsed_map.image is None:
|
||||
return None
|
||||
return None, None
|
||||
img_byte_arr = io.BytesIO()
|
||||
parsed_map.image.data.save(img_byte_arr, format=MAP_FILE_FORMAT)
|
||||
return img_byte_arr.getvalue()
|
||||
return img_byte_arr.getvalue(), parsed_map
|
||||
|
||||
async def _async_setup(self) -> None:
|
||||
"""Set up the coordinator."""
|
||||
@ -206,6 +209,7 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
|
||||
rooms={},
|
||||
image=image,
|
||||
last_updated=dt_util.utcnow() - IMAGE_CACHE_INTERVAL,
|
||||
map_data=None,
|
||||
)
|
||||
for image, roborock_map in zip(stored_images, roborock_maps, strict=False)
|
||||
}
|
||||
@ -230,20 +234,21 @@ class RoborockDataUpdateCoordinator(DataUpdateCoordinator[DeviceProp]):
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="map_failure",
|
||||
)
|
||||
parsed_image = self.parse_image(response)
|
||||
if parsed_image is None:
|
||||
parsed_image, parsed_map = self.parse_map_data_v1(response)
|
||||
if parsed_image is None or parsed_map is None:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="map_failure",
|
||||
)
|
||||
current_roborock_map_info = self.maps[self.current_map]
|
||||
if parsed_image != self.maps[self.current_map].image:
|
||||
await self.map_storage.async_save_map(
|
||||
self.current_map,
|
||||
parsed_image,
|
||||
)
|
||||
current_roborock_map_info = self.maps[self.current_map]
|
||||
current_roborock_map_info.image = parsed_image
|
||||
current_roborock_map_info.last_updated = dt_util.utcnow()
|
||||
current_roborock_map_info.map_data = parsed_map
|
||||
|
||||
async def _verify_api(self) -> None:
|
||||
"""Verify that the api is reachable. If it is not, switch clients."""
|
||||
|
@ -6,6 +6,7 @@ from typing import Any
|
||||
|
||||
from roborock.containers import HomeDataDevice, HomeDataProduct, NetworkInfo
|
||||
from roborock.roborock_typing import DeviceProp
|
||||
from vacuum_map_parser_base.map_data import MapData
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -51,3 +52,11 @@ class RoborockMapInfo:
|
||||
rooms: dict[int, str]
|
||||
image: bytes | None
|
||||
last_updated: datetime
|
||||
map_data: MapData | None
|
||||
|
||||
@property
|
||||
def current_room(self) -> str | None:
|
||||
"""Get the currently active room for this map if any."""
|
||||
if self.map_data is None or self.map_data.vacuum_room is None:
|
||||
return None
|
||||
return self.rooms.get(self.map_data.vacuum_room)
|
||||
|
@ -36,7 +36,11 @@ from .coordinator import (
|
||||
RoborockDataUpdateCoordinator,
|
||||
RoborockDataUpdateCoordinatorA01,
|
||||
)
|
||||
from .entity import RoborockCoordinatedEntityA01, RoborockCoordinatedEntityV1
|
||||
from .entity import (
|
||||
RoborockCoordinatedEntityA01,
|
||||
RoborockCoordinatedEntityV1,
|
||||
RoborockEntity,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
@ -306,7 +310,7 @@ async def async_setup_entry(
|
||||
) -> None:
|
||||
"""Set up the Roborock vacuum sensors."""
|
||||
coordinators = config_entry.runtime_data
|
||||
async_add_entities(
|
||||
entities: list[RoborockEntity] = [
|
||||
RoborockSensorEntity(
|
||||
coordinator,
|
||||
description,
|
||||
@ -314,8 +318,9 @@ async def async_setup_entry(
|
||||
for coordinator in coordinators.v1
|
||||
for description in SENSOR_DESCRIPTIONS
|
||||
if description.value_fn(coordinator.roborock_device_info.props) is not None
|
||||
)
|
||||
async_add_entities(
|
||||
]
|
||||
entities.extend(RoborockCurrentRoom(coordinator) for coordinator in coordinators.v1)
|
||||
entities.extend(
|
||||
RoborockSensorEntityA01(
|
||||
coordinator,
|
||||
description,
|
||||
@ -324,6 +329,7 @@ async def async_setup_entry(
|
||||
for description in A01_SENSOR_DESCRIPTIONS
|
||||
if description.data_protocol in coordinator.data
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class RoborockSensorEntity(RoborockCoordinatedEntityV1, SensorEntity):
|
||||
@ -353,6 +359,42 @@ class RoborockSensorEntity(RoborockCoordinatedEntityV1, SensorEntity):
|
||||
)
|
||||
|
||||
|
||||
class RoborockCurrentRoom(RoborockCoordinatedEntityV1, SensorEntity):
|
||||
"""Representation of a Current Room Sensor."""
|
||||
|
||||
_attr_device_class = SensorDeviceClass.ENUM
|
||||
_attr_translation_key = "current_room"
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: RoborockDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(
|
||||
f"current_room_{coordinator.duid_slug}",
|
||||
coordinator,
|
||||
None,
|
||||
is_dock_entity=False,
|
||||
)
|
||||
|
||||
@property
|
||||
def options(self) -> list[str]:
|
||||
"""Return the currently valid rooms."""
|
||||
if self.coordinator.current_map is not None:
|
||||
return list(
|
||||
self.coordinator.maps[self.coordinator.current_map].rooms.values()
|
||||
)
|
||||
return []
|
||||
|
||||
@property
|
||||
def native_value(self) -> str | None:
|
||||
"""Return the value reported by the sensor."""
|
||||
if self.coordinator.current_map is not None:
|
||||
return self.coordinator.maps[self.coordinator.current_map].current_room
|
||||
return None
|
||||
|
||||
|
||||
class RoborockSensorEntityA01(RoborockCoordinatedEntityA01, SensorEntity):
|
||||
"""Representation of a A01 Roborock sensor."""
|
||||
|
||||
|
@ -181,6 +181,9 @@
|
||||
"countdown": {
|
||||
"name": "Countdown"
|
||||
},
|
||||
"current_room": {
|
||||
"name": "Current room"
|
||||
},
|
||||
"dock_error": {
|
||||
"name": "Dock error",
|
||||
"state": {
|
||||
|
@ -1151,6 +1151,7 @@ MAP_DATA = MapData(0, 0)
|
||||
MAP_DATA.image = ImageData(
|
||||
100, 10, 10, 10, 10, ImageConfig(), Image.new("RGB", (1, 1)), lambda p: p
|
||||
)
|
||||
MAP_DATA.vacuum_room = 17
|
||||
|
||||
|
||||
SCENES = [
|
||||
|
@ -29,7 +29,7 @@ def platforms() -> list[Platform]:
|
||||
|
||||
async def test_sensors(hass: HomeAssistant, setup_entry: MockConfigEntry) -> None:
|
||||
"""Test sensors and check test values are correctly set."""
|
||||
assert len(hass.states.async_all("sensor")) == 40
|
||||
assert len(hass.states.async_all("sensor")) == 42
|
||||
assert hass.states.get("sensor.roborock_s7_maxv_main_brush_time_left").state == str(
|
||||
MAIN_BRUSH_REPLACE_TIME - 74382
|
||||
)
|
||||
@ -63,6 +63,10 @@ async def test_sensors(hass: HomeAssistant, setup_entry: MockConfigEntry) -> Non
|
||||
hass.states.get("sensor.roborock_s7_maxv_last_clean_end").state
|
||||
== "2023-01-01T03:43:58+00:00"
|
||||
)
|
||||
assert (
|
||||
hass.states.get("sensor.roborock_s7_maxv_current_room").state
|
||||
== "Example room 2"
|
||||
)
|
||||
assert hass.states.get("sensor.dyad_pro_status").state == "drying"
|
||||
assert hass.states.get("sensor.dyad_pro_battery").state == "100"
|
||||
assert hass.states.get("sensor.dyad_pro_filter_time_left").state == "111"
|
||||
|
Loading…
x
Reference in New Issue
Block a user