File: rooms.py

package info (click to toggle)
python-roborock 4.12.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,480 kB
  • sloc: python: 16,602; makefile: 17; sh: 6
file content (81 lines) | stat: -rw-r--r-- 3,007 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
"""Trait for managing room mappings on Roborock devices."""

import logging
from dataclasses import dataclass

from roborock.data import HomeData, NamedRoomMapping, RoborockBase
from roborock.devices.traits.v1 import common
from roborock.roborock_typing import RoborockCommand

_LOGGER = logging.getLogger(__name__)

_DEFAULT_NAME = "Unknown"


@dataclass
class Rooms(RoborockBase):
    """Dataclass representing a collection of room mappings."""

    rooms: list[NamedRoomMapping] | None = None
    """List of room mappings."""

    @property
    def room_map(self) -> dict[int, NamedRoomMapping]:
        """Returns a mapping of segment_id to NamedRoomMapping."""
        if self.rooms is None:
            return {}
        return {room.segment_id: room for room in self.rooms}


class RoomsTrait(Rooms, common.V1TraitMixin):
    """Trait for managing the room mappings of Roborock devices."""

    command = RoborockCommand.GET_ROOM_MAPPING

    def __init__(self, home_data: HomeData) -> None:
        """Initialize the RoomsTrait."""
        super().__init__()
        self._home_data = home_data

    @property
    def _iot_id_room_name_map(self) -> dict[str, str]:
        """Returns a dictionary of Room IOT IDs to room names."""
        return {str(room.id): room.name for room in self._home_data.rooms or ()}

    def _parse_response(self, response: common.V1ResponseData) -> Rooms:
        """Parse the response from the device into a list of NamedRoomMapping."""
        if not isinstance(response, list):
            raise ValueError(f"Unexpected RoomsTrait response format: {response!r}")
        name_map = self._iot_id_room_name_map
        segment_pairs = _extract_segment_pairs(response)
        return Rooms(
            rooms=[
                NamedRoomMapping(segment_id=segment_id, iot_id=iot_id, name=name_map.get(iot_id, _DEFAULT_NAME))
                for segment_id, iot_id in segment_pairs
            ]
        )


def _extract_segment_pairs(response: list) -> list[tuple[int, str]]:
    """Extract segment_id and iot_id pairs from the response.

    The response format can be either a flat list of [segment_id, iot_id] or a
    list of lists, where each inner list is a pair of [segment_id, iot_id]. This
    function normalizes the response into a list of (segment_id, iot_id) tuples

    NOTE: We currently only partial samples of the room mapping formats, so
    improving test coverage with samples from a real device with this format
    would be helpful.
    """
    if len(response) == 2 and not isinstance(response[0], list):
        segment_id, iot_id = response[0], response[1]
        return [(segment_id, iot_id)]

    segment_pairs: list[tuple[int, str]] = []
    for part in response:
        if not isinstance(part, list) or len(part) < 2:
            _LOGGER.warning("Unexpected room mapping entry format: %r", part)
            continue
        segment_id, iot_id = part[0], part[1]
        segment_pairs.append((segment_id, iot_id))
    return segment_pairs