-
Notifications
You must be signed in to change notification settings - Fork 73
q7: add map_content trait for B01 current map #785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
487778a
68f41a4
f8c069c
2d73fd1
bedf379
d638f6e
34cebbd
96c42ab
1010ddb
13170e2
741fca6
2e5ede7
1bfc839
3259338
99a63e6
8611c2b
f9efa68
0d43904
a891d3c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """Trait for fetching parsed map content from B01/Q7 devices. | ||
|
|
||
| This intentionally mirrors the v1 `MapContentTrait` contract: | ||
| - `refresh()` performs I/O and populates cached fields | ||
| - `parse_map_content()` reparses cached raw bytes without I/O | ||
| - fields `image_content`, `map_data`, and `raw_api_response` are then readable | ||
|
|
||
| For B01/Q7 devices, the underlying raw map payload is retrieved via `MapTrait`. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove since this will mess with types and is no longer needed for modern code. |
||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| from vacuum_map_parser_base.map_data import MapData | ||
|
|
||
| from roborock.data import RoborockBase | ||
| from roborock.devices.traits import Trait | ||
| from roborock.exceptions import RoborockException | ||
| from roborock.map.b01_map_parser import B01MapParser, B01MapParserConfig | ||
|
|
||
| from .map import MapTrait | ||
|
|
||
| _TRUNCATE_LENGTH = 20 | ||
|
|
||
|
|
||
| @dataclass | ||
| class MapContent(RoborockBase): | ||
| """Dataclass representing map content.""" | ||
|
|
||
| image_content: bytes | None = None | ||
| """The rendered image of the map in PNG format.""" | ||
|
|
||
| map_data: MapData | None = None | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can hold the map image on it right? Or is it not easy to do that with this approach? Still reading through code so I may answer my own question |
||
| """Parsed map data (metadata for points on the map).""" | ||
|
|
||
| raw_api_response: bytes | None = None | ||
| """Raw bytes of the map payload from the device. | ||
|
|
||
| This should be treated as an opaque blob used only internally by this | ||
| library to re-parse the map data when needed. | ||
| """ | ||
|
|
||
| def __repr__(self) -> str: | ||
| img = self.image_content | ||
| if img and len(img) > _TRUNCATE_LENGTH: | ||
| img = img[: _TRUNCATE_LENGTH - 3] + b"..." | ||
| return f"MapContent(image_content={img!r}, map_data={self.map_data!r})" | ||
|
|
||
|
|
||
| class MapContentTrait(MapContent, Trait): | ||
| """Trait for fetching parsed map content for Q7 devices.""" | ||
|
|
||
| def __init__( | ||
| self, | ||
| map_trait: MapTrait, | ||
| *, | ||
| serial: str, | ||
| model: str, | ||
| map_parser_config: B01MapParserConfig | None = None, | ||
| ) -> None: | ||
| super().__init__() | ||
| self._map_trait = map_trait | ||
| self._serial = serial | ||
| self._model = model | ||
| self._map_parser = B01MapParser(map_parser_config) | ||
|
|
||
| async def refresh(self) -> None: | ||
| """Fetch, decode, and parse the current map payload.""" | ||
| raw_payload = await self._map_trait.get_current_map_payload() | ||
| parsed = self.parse_map_content(raw_payload) | ||
| self.image_content = parsed.image_content | ||
| self.map_data = parsed.map_data | ||
| self.raw_api_response = parsed.raw_api_response | ||
|
|
||
| def parse_map_content(self, response: bytes) -> MapContent: | ||
| """Parse map content from raw bytes. | ||
|
|
||
| This mirrors the v1 trait behavior so cached map payload bytes can be | ||
| reparsed without going back to the device. | ||
| """ | ||
| try: | ||
| parsed_data = self._map_parser.parse( | ||
| response, | ||
| serial=self._serial, | ||
| model=self._model, | ||
| ) | ||
| except RoborockException: | ||
| raise | ||
| except Exception as ex: | ||
|
arduano marked this conversation as resolved.
|
||
| raise RoborockException("Failed to parse B01 map data") from ex | ||
|
|
||
| if parsed_data.image_content is None: | ||
| raise RoborockException("Failed to render B01 map image") | ||
|
|
||
| return MapContent( | ||
| image_content=parsed_data.image_content, | ||
| map_data=parsed_data.map_data, | ||
| raw_api_response=response, | ||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI claims regex is the only way, that the python ecosystem has no way of doing lists here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems to be correct per what I see as well