roborock.devices.traits.v1.home
Trait that represents a full view of the home layout.
This trait combines information about maps and rooms to provide a comprehensive view of the home layout, including room names and their corresponding segment on the map. It also makes it straight forward to fetch the map image and data.
This trait depends on the MapsTrait and RoomsTrait to gather the necessary information. It provides properties to access the current map, the list of rooms with names, and the map image and data.
Callers may first call discover_home() to populate the home layout cache by
iterating through all available maps on the device. This will cache the map
information and room names for all maps to minimize map switching and improve
performance. After the initial discovery, callers can call refresh() to update
the current map's information and room names as needed.
1"""Trait that represents a full view of the home layout. 2 3This trait combines information about maps and rooms to provide a comprehensive 4view of the home layout, including room names and their corresponding segment 5on the map. It also makes it straight forward to fetch the map image and data. 6 7This trait depends on the MapsTrait and RoomsTrait to gather the necessary 8information. It provides properties to access the current map, the list of 9rooms with names, and the map image and data. 10 11Callers may first call `discover_home()` to populate the home layout cache by 12iterating through all available maps on the device. This will cache the map 13information and room names for all maps to minimize map switching and improve 14performance. After the initial discovery, callers can call `refresh()` to update 15the current map's information and room names as needed. 16""" 17 18import asyncio 19import base64 20import logging 21 22from roborock.data import CombinedMapInfo, MultiMapsListMapInfo, NamedRoomMapping, RoborockBase 23from roborock.data.v1.v1_code_mappings import RoborockStateCode 24from roborock.devices.cache import DeviceCache 25from roborock.devices.traits.v1 import common 26from roborock.exceptions import RoborockDeviceBusy, RoborockException, RoborockInvalidStatus 27from roborock.roborock_typing import RoborockCommand 28 29from .map_content import MapContent, MapContentTrait 30from .maps import MapsTrait 31from .rooms import RoomsTrait 32from .status import StatusTrait 33 34_LOGGER = logging.getLogger(__name__) 35 36MAP_SLEEP = 3 37 38 39class HomeTrait(RoborockBase, common.V1TraitMixin): 40 """Trait that represents a full view of the home layout.""" 41 42 command = RoborockCommand.GET_MAP_V1 # This is not used 43 converter = common.DefaultConverter(RoborockBase) # Not used 44 45 def __init__( 46 self, 47 status_trait: StatusTrait, 48 maps_trait: MapsTrait, 49 map_content: MapContentTrait, 50 rooms_trait: RoomsTrait, 51 device_cache: DeviceCache, 52 ) -> None: 53 """Initialize the HomeTrait. 54 55 We keep track of the MapsTrait and RoomsTrait to provide a comprehensive 56 view of the home layout. This also depends on the StatusTrait to determine 57 the current map. See comments in MapsTrait for details on that dependency. 58 59 The cache is used to store discovered home data to minimize map switching 60 and improve performance. The cache should be persisted by the caller to 61 ensure data is retained across restarts. 62 63 After initial discovery, only information for the current map is refreshed 64 to keep data up to date without excessive map switching. However, as 65 users switch rooms, the current map's data will be updated to ensure 66 accuracy. 67 """ 68 super().__init__() 69 self._status_trait = status_trait 70 self._maps_trait = maps_trait 71 self._map_content = map_content 72 self._rooms_trait = rooms_trait 73 self._device_cache = device_cache 74 self._discovery_completed = False 75 self._home_map_info: dict[int, CombinedMapInfo] | None = None 76 self._home_map_content: dict[int, MapContent] | None = None 77 78 async def discover_home(self) -> None: 79 """Iterate through all maps to discover rooms and cache them. 80 81 This will be a no-op if the home cache is already populated. 82 83 This cannot be called while the device is cleaning, as that would interrupt the 84 cleaning process. This will raise `RoborockDeviceBusy` if the device is 85 currently cleaning. 86 87 After discovery, the home cache will be populated and can be accessed via the `home_map_info` property. 88 """ 89 device_cache_data = await self._device_cache.get() 90 if device_cache_data and device_cache_data.home_map_info: 91 _LOGGER.debug("Home cache already populated, skipping discovery") 92 self._home_map_info = device_cache_data.home_map_info 93 self._discovery_completed = True 94 try: 95 self._home_map_content = { 96 k: self._map_content.converter.parse_map_content(base64.b64decode(v)) 97 for k, v in (device_cache_data.home_map_content_base64 or {}).items() 98 } 99 except (ValueError, RoborockException) as ex: 100 _LOGGER.warning("Failed to parse cached home map content, will re-discover: %s", ex) 101 self._home_map_content = {} 102 else: 103 return 104 105 if self._status_trait.state == RoborockStateCode.cleaning: 106 raise RoborockDeviceBusy("Cannot perform home discovery while the device is cleaning") 107 108 await self._maps_trait.refresh() 109 if self._maps_trait.current_map_info is None: 110 raise RoborockException("Cannot perform home discovery without current map info") 111 112 home_map_info, home_map_content = await self._build_home_map_info() 113 _LOGGER.debug("Home discovery complete, caching data for %d maps", len(home_map_info)) 114 self._discovery_completed = True 115 await self._update_home_cache(home_map_info, home_map_content) 116 117 async def _refresh_map_info(self, map_info: MultiMapsListMapInfo) -> CombinedMapInfo: 118 """Collect room data for a specific map and return CombinedMapInfo.""" 119 await self._rooms_trait.refresh() 120 121 # We have room names from multiple sources: 122 # - The map_info.rooms which we just received from the MultiMapsList 123 # - RoomsTrait rooms come from the GET_ROOM_MAPPING command for the current device (only) 124 # - RoomsTrait rooms that are pulled from the cloud API 125 # We always prefer the RoomsTrait room names since they are always newer and 126 # just refreshed above. 127 rooms_map: dict[int, NamedRoomMapping] = { 128 **map_info.rooms_map, 129 **{room.segment_id: room for room in self._rooms_trait.rooms or ()}, 130 } 131 return CombinedMapInfo( 132 map_flag=map_info.map_flag, 133 name=map_info.name, 134 rooms=list(rooms_map.values()), 135 ) 136 137 async def _refresh_map_content(self) -> MapContent: 138 """Refresh the map content trait to get the latest map data.""" 139 await self._map_content.refresh() 140 return MapContent( 141 image_content=self._map_content.image_content, 142 map_data=self._map_content.map_data, 143 raw_api_response=self._map_content.raw_api_response, 144 ) 145 146 async def _build_home_map_info(self) -> tuple[dict[int, CombinedMapInfo], dict[int, MapContent]]: 147 """Perform the actual discovery and caching of home map info and content.""" 148 home_map_info: dict[int, CombinedMapInfo] = {} 149 home_map_content: dict[int, MapContent] = {} 150 151 # Sort map_info to process the current map last, reducing map switching. 152 # False (non-original maps) sorts before True (original map). We ensure 153 # we load the original map last. 154 sorted_map_infos = sorted( 155 self._maps_trait.map_info or [], 156 key=lambda mi: mi.map_flag == self._maps_trait.current_map, 157 reverse=False, 158 ) 159 _LOGGER.debug("Building home cache for maps: %s", [mi.map_flag for mi in sorted_map_infos]) 160 for map_info in sorted_map_infos: 161 # We need to load each map to get its room data 162 if len(sorted_map_infos) > 1: 163 _LOGGER.debug("Loading map %s", map_info.map_flag) 164 try: 165 await self._maps_trait.set_current_map(map_info.map_flag) 166 except RoborockInvalidStatus as ex: 167 # Device is in a state that forbids map switching. Translate to 168 # "busy" so callers can fall back to refreshing the current map only. 169 raise RoborockDeviceBusy("Cannot switch maps right now (device action locked)") from ex 170 await asyncio.sleep(MAP_SLEEP) 171 172 map_content = await self._refresh_map_content() 173 home_map_content[map_info.map_flag] = map_content 174 175 combined_map_info = await self._refresh_map_info(map_info) 176 home_map_info[map_info.map_flag] = combined_map_info 177 return home_map_info, home_map_content 178 179 async def refresh(self) -> None: 180 """Refresh current map's underlying map and room data, updating cache as needed. 181 182 This will only refresh the current map's data and will not populate non 183 active maps or re-discover the home. It is expected that this will keep 184 information up to date for the current map as users switch to that map. 185 """ 186 if not self._discovery_completed: 187 # Running initial discovery also populates all of the same information 188 # as below so we can just call that method. If the device is busy 189 # then we'll fall through below to refresh the current map only. 190 try: 191 await self.discover_home() 192 return 193 except RoborockDeviceBusy: 194 _LOGGER.debug("Cannot refresh home data while device is busy cleaning") 195 196 # Refresh the list of map names/info 197 await self._maps_trait.refresh() 198 if (current_map_info := self._maps_trait.current_map_info) is None or ( 199 map_flag := self._maps_trait.current_map 200 ) is None: 201 raise RoborockException("Cannot refresh home data without current map info") 202 203 # Refresh the map content to ensure we have the latest image and object positions 204 new_map_content = await self._refresh_map_content() 205 # Refresh the current map's room data 206 combined_map_info = await self._refresh_map_info(current_map_info) 207 await self._update_current_map( 208 map_flag, combined_map_info, new_map_content, update_cache=self._discovery_completed 209 ) 210 211 @property 212 def home_map_info(self) -> dict[int, CombinedMapInfo] | None: 213 """Returns the map information for all cached maps.""" 214 return self._home_map_info 215 216 @property 217 def current_map_data(self) -> CombinedMapInfo | None: 218 """Returns the map data for the current map.""" 219 current_map_flag = self._maps_trait.current_map 220 if current_map_flag is None or self._home_map_info is None: 221 return None 222 return self._home_map_info.get(current_map_flag) 223 224 @property 225 def current_rooms(self) -> list[NamedRoomMapping]: 226 """Returns the room names for the current map.""" 227 if self.current_map_data is None: 228 return [] 229 return self.current_map_data.rooms 230 231 @property 232 def home_map_content(self) -> dict[int, MapContent] | None: 233 """Returns the map content for all cached maps.""" 234 return self._home_map_content 235 236 async def _update_home_cache( 237 self, home_map_info: dict[int, CombinedMapInfo], home_map_content: dict[int, MapContent] 238 ) -> None: 239 """Update the entire home cache with new map info and content.""" 240 device_cache_data = await self._device_cache.get() 241 device_cache_data.home_map_info = home_map_info 242 device_cache_data.home_map_content_base64 = { 243 k: base64.b64encode(v.raw_api_response).decode("utf-8") 244 for k, v in home_map_content.items() 245 if v.raw_api_response 246 } 247 await self._device_cache.set(device_cache_data) 248 self._home_map_info = home_map_info 249 self._home_map_content = home_map_content 250 251 async def _update_current_map( 252 self, 253 map_flag: int, 254 map_info: CombinedMapInfo, 255 map_content: MapContent, 256 update_cache: bool, 257 ) -> None: 258 """Update the cache for the current map only.""" 259 # Update the persistent cache if requested e.g. home discovery has 260 # completed and we want to keep it fresh. Otherwise just update the 261 # in memory map below. 262 if update_cache: 263 device_cache_data = await self._device_cache.get() 264 if device_cache_data.home_map_info is None: 265 device_cache_data.home_map_info = {} 266 device_cache_data.home_map_info[map_flag] = map_info 267 if map_content.raw_api_response: 268 if device_cache_data.home_map_content_base64 is None: 269 device_cache_data.home_map_content_base64 = {} 270 device_cache_data.home_map_content_base64[map_flag] = base64.b64encode( 271 map_content.raw_api_response 272 ).decode("utf-8") 273 await self._device_cache.set(device_cache_data) 274 275 if self._home_map_info is None: 276 self._home_map_info = {} 277 self._home_map_info[map_flag] = map_info 278 279 if self._home_map_content is None: 280 self._home_map_content = {} 281 self._home_map_content[map_flag] = map_content
40class HomeTrait(RoborockBase, common.V1TraitMixin): 41 """Trait that represents a full view of the home layout.""" 42 43 command = RoborockCommand.GET_MAP_V1 # This is not used 44 converter = common.DefaultConverter(RoborockBase) # Not used 45 46 def __init__( 47 self, 48 status_trait: StatusTrait, 49 maps_trait: MapsTrait, 50 map_content: MapContentTrait, 51 rooms_trait: RoomsTrait, 52 device_cache: DeviceCache, 53 ) -> None: 54 """Initialize the HomeTrait. 55 56 We keep track of the MapsTrait and RoomsTrait to provide a comprehensive 57 view of the home layout. This also depends on the StatusTrait to determine 58 the current map. See comments in MapsTrait for details on that dependency. 59 60 The cache is used to store discovered home data to minimize map switching 61 and improve performance. The cache should be persisted by the caller to 62 ensure data is retained across restarts. 63 64 After initial discovery, only information for the current map is refreshed 65 to keep data up to date without excessive map switching. However, as 66 users switch rooms, the current map's data will be updated to ensure 67 accuracy. 68 """ 69 super().__init__() 70 self._status_trait = status_trait 71 self._maps_trait = maps_trait 72 self._map_content = map_content 73 self._rooms_trait = rooms_trait 74 self._device_cache = device_cache 75 self._discovery_completed = False 76 self._home_map_info: dict[int, CombinedMapInfo] | None = None 77 self._home_map_content: dict[int, MapContent] | None = None 78 79 async def discover_home(self) -> None: 80 """Iterate through all maps to discover rooms and cache them. 81 82 This will be a no-op if the home cache is already populated. 83 84 This cannot be called while the device is cleaning, as that would interrupt the 85 cleaning process. This will raise `RoborockDeviceBusy` if the device is 86 currently cleaning. 87 88 After discovery, the home cache will be populated and can be accessed via the `home_map_info` property. 89 """ 90 device_cache_data = await self._device_cache.get() 91 if device_cache_data and device_cache_data.home_map_info: 92 _LOGGER.debug("Home cache already populated, skipping discovery") 93 self._home_map_info = device_cache_data.home_map_info 94 self._discovery_completed = True 95 try: 96 self._home_map_content = { 97 k: self._map_content.converter.parse_map_content(base64.b64decode(v)) 98 for k, v in (device_cache_data.home_map_content_base64 or {}).items() 99 } 100 except (ValueError, RoborockException) as ex: 101 _LOGGER.warning("Failed to parse cached home map content, will re-discover: %s", ex) 102 self._home_map_content = {} 103 else: 104 return 105 106 if self._status_trait.state == RoborockStateCode.cleaning: 107 raise RoborockDeviceBusy("Cannot perform home discovery while the device is cleaning") 108 109 await self._maps_trait.refresh() 110 if self._maps_trait.current_map_info is None: 111 raise RoborockException("Cannot perform home discovery without current map info") 112 113 home_map_info, home_map_content = await self._build_home_map_info() 114 _LOGGER.debug("Home discovery complete, caching data for %d maps", len(home_map_info)) 115 self._discovery_completed = True 116 await self._update_home_cache(home_map_info, home_map_content) 117 118 async def _refresh_map_info(self, map_info: MultiMapsListMapInfo) -> CombinedMapInfo: 119 """Collect room data for a specific map and return CombinedMapInfo.""" 120 await self._rooms_trait.refresh() 121 122 # We have room names from multiple sources: 123 # - The map_info.rooms which we just received from the MultiMapsList 124 # - RoomsTrait rooms come from the GET_ROOM_MAPPING command for the current device (only) 125 # - RoomsTrait rooms that are pulled from the cloud API 126 # We always prefer the RoomsTrait room names since they are always newer and 127 # just refreshed above. 128 rooms_map: dict[int, NamedRoomMapping] = { 129 **map_info.rooms_map, 130 **{room.segment_id: room for room in self._rooms_trait.rooms or ()}, 131 } 132 return CombinedMapInfo( 133 map_flag=map_info.map_flag, 134 name=map_info.name, 135 rooms=list(rooms_map.values()), 136 ) 137 138 async def _refresh_map_content(self) -> MapContent: 139 """Refresh the map content trait to get the latest map data.""" 140 await self._map_content.refresh() 141 return MapContent( 142 image_content=self._map_content.image_content, 143 map_data=self._map_content.map_data, 144 raw_api_response=self._map_content.raw_api_response, 145 ) 146 147 async def _build_home_map_info(self) -> tuple[dict[int, CombinedMapInfo], dict[int, MapContent]]: 148 """Perform the actual discovery and caching of home map info and content.""" 149 home_map_info: dict[int, CombinedMapInfo] = {} 150 home_map_content: dict[int, MapContent] = {} 151 152 # Sort map_info to process the current map last, reducing map switching. 153 # False (non-original maps) sorts before True (original map). We ensure 154 # we load the original map last. 155 sorted_map_infos = sorted( 156 self._maps_trait.map_info or [], 157 key=lambda mi: mi.map_flag == self._maps_trait.current_map, 158 reverse=False, 159 ) 160 _LOGGER.debug("Building home cache for maps: %s", [mi.map_flag for mi in sorted_map_infos]) 161 for map_info in sorted_map_infos: 162 # We need to load each map to get its room data 163 if len(sorted_map_infos) > 1: 164 _LOGGER.debug("Loading map %s", map_info.map_flag) 165 try: 166 await self._maps_trait.set_current_map(map_info.map_flag) 167 except RoborockInvalidStatus as ex: 168 # Device is in a state that forbids map switching. Translate to 169 # "busy" so callers can fall back to refreshing the current map only. 170 raise RoborockDeviceBusy("Cannot switch maps right now (device action locked)") from ex 171 await asyncio.sleep(MAP_SLEEP) 172 173 map_content = await self._refresh_map_content() 174 home_map_content[map_info.map_flag] = map_content 175 176 combined_map_info = await self._refresh_map_info(map_info) 177 home_map_info[map_info.map_flag] = combined_map_info 178 return home_map_info, home_map_content 179 180 async def refresh(self) -> None: 181 """Refresh current map's underlying map and room data, updating cache as needed. 182 183 This will only refresh the current map's data and will not populate non 184 active maps or re-discover the home. It is expected that this will keep 185 information up to date for the current map as users switch to that map. 186 """ 187 if not self._discovery_completed: 188 # Running initial discovery also populates all of the same information 189 # as below so we can just call that method. If the device is busy 190 # then we'll fall through below to refresh the current map only. 191 try: 192 await self.discover_home() 193 return 194 except RoborockDeviceBusy: 195 _LOGGER.debug("Cannot refresh home data while device is busy cleaning") 196 197 # Refresh the list of map names/info 198 await self._maps_trait.refresh() 199 if (current_map_info := self._maps_trait.current_map_info) is None or ( 200 map_flag := self._maps_trait.current_map 201 ) is None: 202 raise RoborockException("Cannot refresh home data without current map info") 203 204 # Refresh the map content to ensure we have the latest image and object positions 205 new_map_content = await self._refresh_map_content() 206 # Refresh the current map's room data 207 combined_map_info = await self._refresh_map_info(current_map_info) 208 await self._update_current_map( 209 map_flag, combined_map_info, new_map_content, update_cache=self._discovery_completed 210 ) 211 212 @property 213 def home_map_info(self) -> dict[int, CombinedMapInfo] | None: 214 """Returns the map information for all cached maps.""" 215 return self._home_map_info 216 217 @property 218 def current_map_data(self) -> CombinedMapInfo | None: 219 """Returns the map data for the current map.""" 220 current_map_flag = self._maps_trait.current_map 221 if current_map_flag is None or self._home_map_info is None: 222 return None 223 return self._home_map_info.get(current_map_flag) 224 225 @property 226 def current_rooms(self) -> list[NamedRoomMapping]: 227 """Returns the room names for the current map.""" 228 if self.current_map_data is None: 229 return [] 230 return self.current_map_data.rooms 231 232 @property 233 def home_map_content(self) -> dict[int, MapContent] | None: 234 """Returns the map content for all cached maps.""" 235 return self._home_map_content 236 237 async def _update_home_cache( 238 self, home_map_info: dict[int, CombinedMapInfo], home_map_content: dict[int, MapContent] 239 ) -> None: 240 """Update the entire home cache with new map info and content.""" 241 device_cache_data = await self._device_cache.get() 242 device_cache_data.home_map_info = home_map_info 243 device_cache_data.home_map_content_base64 = { 244 k: base64.b64encode(v.raw_api_response).decode("utf-8") 245 for k, v in home_map_content.items() 246 if v.raw_api_response 247 } 248 await self._device_cache.set(device_cache_data) 249 self._home_map_info = home_map_info 250 self._home_map_content = home_map_content 251 252 async def _update_current_map( 253 self, 254 map_flag: int, 255 map_info: CombinedMapInfo, 256 map_content: MapContent, 257 update_cache: bool, 258 ) -> None: 259 """Update the cache for the current map only.""" 260 # Update the persistent cache if requested e.g. home discovery has 261 # completed and we want to keep it fresh. Otherwise just update the 262 # in memory map below. 263 if update_cache: 264 device_cache_data = await self._device_cache.get() 265 if device_cache_data.home_map_info is None: 266 device_cache_data.home_map_info = {} 267 device_cache_data.home_map_info[map_flag] = map_info 268 if map_content.raw_api_response: 269 if device_cache_data.home_map_content_base64 is None: 270 device_cache_data.home_map_content_base64 = {} 271 device_cache_data.home_map_content_base64[map_flag] = base64.b64encode( 272 map_content.raw_api_response 273 ).decode("utf-8") 274 await self._device_cache.set(device_cache_data) 275 276 if self._home_map_info is None: 277 self._home_map_info = {} 278 self._home_map_info[map_flag] = map_info 279 280 if self._home_map_content is None: 281 self._home_map_content = {} 282 self._home_map_content[map_flag] = map_content
Trait that represents a full view of the home layout.
46 def __init__( 47 self, 48 status_trait: StatusTrait, 49 maps_trait: MapsTrait, 50 map_content: MapContentTrait, 51 rooms_trait: RoomsTrait, 52 device_cache: DeviceCache, 53 ) -> None: 54 """Initialize the HomeTrait. 55 56 We keep track of the MapsTrait and RoomsTrait to provide a comprehensive 57 view of the home layout. This also depends on the StatusTrait to determine 58 the current map. See comments in MapsTrait for details on that dependency. 59 60 The cache is used to store discovered home data to minimize map switching 61 and improve performance. The cache should be persisted by the caller to 62 ensure data is retained across restarts. 63 64 After initial discovery, only information for the current map is refreshed 65 to keep data up to date without excessive map switching. However, as 66 users switch rooms, the current map's data will be updated to ensure 67 accuracy. 68 """ 69 super().__init__() 70 self._status_trait = status_trait 71 self._maps_trait = maps_trait 72 self._map_content = map_content 73 self._rooms_trait = rooms_trait 74 self._device_cache = device_cache 75 self._discovery_completed = False 76 self._home_map_info: dict[int, CombinedMapInfo] | None = None 77 self._home_map_content: dict[int, MapContent] | None = None
Initialize the HomeTrait.
We keep track of the MapsTrait and RoomsTrait to provide a comprehensive view of the home layout. This also depends on the StatusTrait to determine the current map. See comments in MapsTrait for details on that dependency.
The cache is used to store discovered home data to minimize map switching and improve performance. The cache should be persisted by the caller to ensure data is retained across restarts.
After initial discovery, only information for the current map is refreshed to keep data up to date without excessive map switching. However, as users switch rooms, the current map's data will be updated to ensure accuracy.
The RoborockCommand used to fetch the trait data from the device (internal only).
The converter used to parse the response from the device (internal only).
79 async def discover_home(self) -> None: 80 """Iterate through all maps to discover rooms and cache them. 81 82 This will be a no-op if the home cache is already populated. 83 84 This cannot be called while the device is cleaning, as that would interrupt the 85 cleaning process. This will raise `RoborockDeviceBusy` if the device is 86 currently cleaning. 87 88 After discovery, the home cache will be populated and can be accessed via the `home_map_info` property. 89 """ 90 device_cache_data = await self._device_cache.get() 91 if device_cache_data and device_cache_data.home_map_info: 92 _LOGGER.debug("Home cache already populated, skipping discovery") 93 self._home_map_info = device_cache_data.home_map_info 94 self._discovery_completed = True 95 try: 96 self._home_map_content = { 97 k: self._map_content.converter.parse_map_content(base64.b64decode(v)) 98 for k, v in (device_cache_data.home_map_content_base64 or {}).items() 99 } 100 except (ValueError, RoborockException) as ex: 101 _LOGGER.warning("Failed to parse cached home map content, will re-discover: %s", ex) 102 self._home_map_content = {} 103 else: 104 return 105 106 if self._status_trait.state == RoborockStateCode.cleaning: 107 raise RoborockDeviceBusy("Cannot perform home discovery while the device is cleaning") 108 109 await self._maps_trait.refresh() 110 if self._maps_trait.current_map_info is None: 111 raise RoborockException("Cannot perform home discovery without current map info") 112 113 home_map_info, home_map_content = await self._build_home_map_info() 114 _LOGGER.debug("Home discovery complete, caching data for %d maps", len(home_map_info)) 115 self._discovery_completed = True 116 await self._update_home_cache(home_map_info, home_map_content)
Iterate through all maps to discover rooms and cache them.
This will be a no-op if the home cache is already populated.
This cannot be called while the device is cleaning, as that would interrupt the
cleaning process. This will raise RoborockDeviceBusy if the device is
currently cleaning.
After discovery, the home cache will be populated and can be accessed via the home_map_info property.
180 async def refresh(self) -> None: 181 """Refresh current map's underlying map and room data, updating cache as needed. 182 183 This will only refresh the current map's data and will not populate non 184 active maps or re-discover the home. It is expected that this will keep 185 information up to date for the current map as users switch to that map. 186 """ 187 if not self._discovery_completed: 188 # Running initial discovery also populates all of the same information 189 # as below so we can just call that method. If the device is busy 190 # then we'll fall through below to refresh the current map only. 191 try: 192 await self.discover_home() 193 return 194 except RoborockDeviceBusy: 195 _LOGGER.debug("Cannot refresh home data while device is busy cleaning") 196 197 # Refresh the list of map names/info 198 await self._maps_trait.refresh() 199 if (current_map_info := self._maps_trait.current_map_info) is None or ( 200 map_flag := self._maps_trait.current_map 201 ) is None: 202 raise RoborockException("Cannot refresh home data without current map info") 203 204 # Refresh the map content to ensure we have the latest image and object positions 205 new_map_content = await self._refresh_map_content() 206 # Refresh the current map's room data 207 combined_map_info = await self._refresh_map_info(current_map_info) 208 await self._update_current_map( 209 map_flag, combined_map_info, new_map_content, update_cache=self._discovery_completed 210 )
Refresh current map's underlying map and room data, updating cache as needed.
This will only refresh the current map's data and will not populate non active maps or re-discover the home. It is expected that this will keep information up to date for the current map as users switch to that map.
212 @property 213 def home_map_info(self) -> dict[int, CombinedMapInfo] | None: 214 """Returns the map information for all cached maps.""" 215 return self._home_map_info
Returns the map information for all cached maps.
217 @property 218 def current_map_data(self) -> CombinedMapInfo | None: 219 """Returns the map data for the current map.""" 220 current_map_flag = self._maps_trait.current_map 221 if current_map_flag is None or self._home_map_info is None: 222 return None 223 return self._home_map_info.get(current_map_flag)
Returns the map data for the current map.
225 @property 226 def current_rooms(self) -> list[NamedRoomMapping]: 227 """Returns the room names for the current map.""" 228 if self.current_map_data is None: 229 return [] 230 return self.current_map_data.rooms
Returns the room names for the current map.
232 @property 233 def home_map_content(self) -> dict[int, MapContent] | None: 234 """Returns the map content for all cached maps.""" 235 return self._home_map_content
Returns the map content for all cached maps.