Skip to content

Commit 4306fbe

Browse files
authored
Fix regression in roborock image entity naming (home-assistant#157432)
1 parent 6f4c479 commit 4306fbe

File tree

4 files changed

+133
-31
lines changed

4 files changed

+133
-31
lines changed

homeassistant/components/roborock/image.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ async def async_setup_entry(
3232
(
3333
RoborockMap(
3434
config_entry,
35-
f"{coord.duid_slug}_map_{map_info.name}",
3635
coord,
3736
coord.properties_api.home,
3837
map_info.map_flag,
@@ -55,13 +54,17 @@ class RoborockMap(RoborockCoordinatedEntityV1, ImageEntity):
5554
def __init__(
5655
self,
5756
config_entry: ConfigEntry,
58-
unique_id: str,
5957
coordinator: RoborockDataUpdateCoordinator,
6058
home_trait: HomeTrait,
6159
map_flag: int,
6260
map_name: str,
6361
) -> None:
6462
"""Initialize a Roborock map."""
63+
map_name = map_name or f"Map {map_flag}"
64+
# Note: Map names are not a valid unique id since they can be changed
65+
# in the roborock app. This should be migrated to use map flag for
66+
# the unique id.
67+
unique_id = f"{coordinator.duid_slug}_map_{map_name}"
6568
RoborockCoordinatedEntityV1.__init__(self, unique_id, coordinator)
6669
ImageEntity.__init__(self, coordinator.hass)
6770
self.config_entry = config_entry

tests/components/roborock/conftest.py

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from unittest.mock import AsyncMock, Mock, PropertyMock, patch
1111

1212
import pytest
13-
from roborock import RoborockCategory
13+
from roborock import HomeDataRoom, MultiMapsListMapInfo, RoborockCategory
1414
from roborock.data import (
1515
CombinedMapInfo,
1616
DnDTimer,
@@ -199,6 +199,41 @@ async def set_dnd_timer(timer: DnDTimer) -> None:
199199
return dnd_trait
200200

201201

202+
def make_home_trait(
203+
map_info: list[MultiMapsListMapInfo],
204+
current_map: int | None,
205+
room_mapping: dict[int, int],
206+
rooms: list[HomeDataRoom],
207+
) -> AsyncMock:
208+
"""Create a mock roborock home trait."""
209+
home_trait = make_mock_trait(trait_spec=HomeTrait)
210+
home_map_info = {
211+
map_data.map_flag: CombinedMapInfo(
212+
name=map_data.name,
213+
map_flag=map_data.map_flag,
214+
rooms=[
215+
NamedRoomMapping(
216+
segment_id=room_mapping[room.id],
217+
iot_id=room.id,
218+
name=room.name,
219+
)
220+
for room in rooms
221+
],
222+
)
223+
for map_data in map_info
224+
}
225+
home_map_content = {
226+
map_data.map_flag: MapContent(
227+
image_content=b"\x89PNG-001", map_data=deepcopy(MAP_DATA)
228+
)
229+
for map_data in map_info
230+
}
231+
home_trait.home_map_info = home_map_info
232+
home_trait.current_map_data = home_map_info[current_map]
233+
home_trait.home_map_content = home_map_content
234+
return home_trait
235+
236+
202237
def create_v1_properties(network_info: NetworkInfo) -> AsyncMock:
203238
"""Create v1 properties for each fake device."""
204239
v1_properties = AsyncMock(spec=PropertiesApi)
@@ -239,31 +274,12 @@ def create_v1_properties(network_info: NetworkInfo) -> AsyncMock:
239274
)
240275
v1_properties.wash_towel_mode = make_mock_trait(trait_spec=WashTowelModeTrait)
241276
v1_properties.smart_wash_params = make_mock_trait(trait_spec=SmartWashParamsTrait)
242-
v1_properties.home = make_mock_trait(trait_spec=HomeTrait)
243-
home_map_info = {
244-
map_data.map_flag: CombinedMapInfo(
245-
name=map_data.name,
246-
map_flag=map_data.map_flag,
247-
rooms=[
248-
NamedRoomMapping(
249-
segment_id=ROOM_MAPPING[room.id],
250-
iot_id=room.id,
251-
name=room.name,
252-
)
253-
for room in HOME_DATA.rooms
254-
],
255-
)
256-
for map_data in MULTI_MAP_LIST.map_info
257-
}
258-
home_map_content = {
259-
map_data.map_flag: MapContent(
260-
image_content=b"\x89PNG-001", map_data=deepcopy(MAP_DATA)
261-
)
262-
for map_data in MULTI_MAP_LIST.map_info
263-
}
264-
v1_properties.home.home_map_info = home_map_info
265-
v1_properties.home.current_map_data = home_map_info[STATUS.current_map]
266-
v1_properties.home.home_map_content = home_map_content
277+
v1_properties.home = make_home_trait(
278+
map_info=MULTI_MAP_LIST.map_info,
279+
current_map=STATUS.current_map,
280+
room_mapping=ROOM_MAPPING,
281+
rooms=HOME_DATA.rooms,
282+
)
267283
v1_properties.network_info = make_mock_trait(
268284
trait_spec=NetworkInfoTrait,
269285
dataclass_template=network_info,

tests/components/roborock/mock_data.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,29 @@
11531153
],
11541154
}
11551155
)
1156+
MULTI_MAP_LIST_NO_MAP_NAMES = MultiMapsList.from_dict(
1157+
{
1158+
"maxMultiMap": 4,
1159+
"maxBakMap": 1,
1160+
"multiMapCount": 2,
1161+
"mapInfo": [
1162+
{
1163+
"mapFlag": 0,
1164+
"addTime": 1686235489,
1165+
"length": 0,
1166+
"name": "",
1167+
"bakMaps": [{"addTime": 1673304288}],
1168+
},
1169+
{
1170+
"mapFlag": 1,
1171+
"addTime": 1697579901,
1172+
"length": 0,
1173+
"name": "",
1174+
"bakMaps": [{"addTime": 1695521431}],
1175+
},
1176+
],
1177+
}
1178+
)
11561179

11571180
MAP_DATA = MapData(0, 0)
11581181
MAP_DATA.image = ImageData(

tests/components/roborock/test_image.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from unittest.mock import patch
88

99
import pytest
10-
from roborock import RoborockException
10+
from roborock import MultiMapsList, RoborockException
1111
from roborock.data import RoborockStateCode
1212
from roborock.devices.traits.v1.map_content import MapContent
1313

@@ -16,8 +16,15 @@
1616
from homeassistant.core import HomeAssistant
1717
from homeassistant.util import dt as dt_util
1818

19-
from .conftest import FakeDevice
20-
from .mock_data import MAP_DATA
19+
from .conftest import FakeDevice, make_home_trait
20+
from .mock_data import (
21+
HOME_DATA,
22+
MAP_DATA,
23+
MULTI_MAP_LIST,
24+
MULTI_MAP_LIST_NO_MAP_NAMES,
25+
ROOM_MAPPING,
26+
STATUS,
27+
)
2128

2229
from tests.common import MockConfigEntry, async_fire_time_changed
2330
from tests.typing import ClientSessionGenerator
@@ -161,3 +168,56 @@ async def test_map_status_change(
161168
body = await resp.read()
162169
assert body is not None
163170
assert body != old_body
171+
172+
173+
@pytest.mark.parametrize(
174+
("multi_maps_list", "expected_entity_ids"),
175+
[
176+
(
177+
MULTI_MAP_LIST,
178+
{
179+
"image.roborock_s7_2_downstairs",
180+
"image.roborock_s7_2_upstairs",
181+
"image.roborock_s7_maxv_downstairs",
182+
"image.roborock_s7_maxv_upstairs",
183+
},
184+
),
185+
(
186+
MULTI_MAP_LIST_NO_MAP_NAMES,
187+
{
188+
"image.roborock_s7_2_downstairs",
189+
"image.roborock_s7_2_upstairs",
190+
# Expect default names based on map flags
191+
"image.roborock_s7_maxv_map_0",
192+
"image.roborock_s7_maxv_map_1",
193+
},
194+
),
195+
],
196+
)
197+
async def test_image_entity_naming(
198+
hass: HomeAssistant,
199+
hass_client: ClientSessionGenerator,
200+
mock_roborock_entry: MockConfigEntry,
201+
fake_vacuum: FakeDevice,
202+
multi_maps_list: MultiMapsList,
203+
expected_entity_ids: set[str],
204+
) -> None:
205+
"""Test entity naming when no map name is set."""
206+
# Override one of the vacuums multi map list response based on the
207+
# test parameterization
208+
assert fake_vacuum.v1_properties
209+
fake_vacuum.v1_properties.home = make_home_trait(
210+
map_info=multi_maps_list.map_info or [],
211+
current_map=STATUS.current_map,
212+
room_mapping=ROOM_MAPPING,
213+
rooms=HOME_DATA.rooms,
214+
)
215+
216+
# Setup the config entry
217+
await hass.config_entries.async_setup(mock_roborock_entry.entry_id)
218+
await hass.async_block_till_done()
219+
220+
# Verify the image entities are created with the expected names
221+
assert {
222+
state.entity_id for state in hass.states.async_all("image")
223+
} == expected_entity_ids

0 commit comments

Comments
 (0)