44until the API is stable.
55"""
66
7- import enum
87import logging
9- from collections .abc import Callable
10- from functools import cached_property
11-
12- from roborock .containers import (
13- HomeDataDevice ,
14- HomeDataProduct ,
15- ModelStatus ,
16- S7MaxVStatus ,
17- Status ,
18- UserData ,
19- )
8+ from abc import ABC
9+ from collections .abc import Callable , Mapping
10+ from types import MappingProxyType
11+
12+ from roborock .containers import HomeDataDevice
2013from roborock .roborock_message import RoborockMessage
21- from roborock .roborock_typing import RoborockCommand
2214
23- from .v1_channel import V1Channel
15+ from .channel import Channel
16+ from .traits .trait import Trait
2417
2518_LOGGER = logging .getLogger (__name__ )
2619
2720__all__ = [
2821 "RoborockDevice" ,
29- "DeviceVersion" ,
3022]
3123
3224
33- class DeviceVersion (enum .StrEnum ):
34- """Enum for device versions."""
35-
36- V1 = "1.0"
37- A01 = "A01"
38- UNKNOWN = "unknown"
39-
25+ class RoborockDevice (ABC ):
26+ """A generic channel for establishing a connection with a Roborock device.
4027
41- class RoborockDevice :
42- """Unified Roborock device class with automatic connection setup."""
28+ Individual channel implementations have their own methods for speaking to
29+ the device that hide some of the protocol specific complexity, but they
30+ are still specialized for the device type and protocol.
31+ """
4332
4433 def __init__ (
4534 self ,
46- user_data : UserData ,
4735 device_info : HomeDataDevice ,
48- product_info : HomeDataProduct ,
49- v1_channel : V1Channel ,
36+ channel : Channel ,
37+ traits : list [ Trait ] ,
5038 ) -> None :
5139 """Initialize the RoborockDevice.
5240
53- The device takes ownership of the V1 channel for communication with the device.
41+ The device takes ownership of the channel for communication with the device.
5442 Use `connect()` to establish the connection, which will set up the appropriate
5543 protocol channel. Use `close()` to clean up all connections.
5644 """
57- self ._user_data = user_data
58- self ._device_info = device_info
59- self ._product_info = product_info
60- self ._v1_channel = v1_channel
45+ self ._duid = device_info .duid
46+ self ._name = device_info .name
47+ self ._channel = channel
6148 self ._unsub : Callable [[], None ] | None = None
49+ self ._trait_map = {trait .name : trait for trait in traits }
50+ if len (self ._trait_map ) != len (traits ):
51+ raise ValueError ("Duplicate trait names found in traits list" )
6252
6353 @property
6454 def duid (self ) -> str :
6555 """Return the device unique identifier (DUID)."""
66- return self ._device_info . duid
56+ return self ._duid
6757
6858 @property
6959 def name (self ) -> str :
7060 """Return the device name."""
71- return self ._device_info .name
72-
73- @cached_property
74- def device_version (self ) -> str :
75- """Return the device version.
76-
77- At the moment this is a simple check against the product version (pv) of the device
78- and used as a placeholder for upcoming functionality for devices that will behave
79- differently based on the version and capabilities.
80- """
81- if self ._device_info .pv == DeviceVersion .V1 .value :
82- return DeviceVersion .V1
83- elif self ._device_info .pv == DeviceVersion .A01 .value :
84- return DeviceVersion .A01
85- _LOGGER .warning (
86- "Unknown device version %s for device %s, using default UNKNOWN" ,
87- self ._device_info .pv ,
88- self ._device_info .name ,
89- )
90- return DeviceVersion .UNKNOWN
61+ return self ._name
9162
9263 @property
9364 def is_connected (self ) -> bool :
9465 """Return whether the device is connected."""
95- return self ._v1_channel . is_mqtt_connected or self . _v1_channel . is_local_connected
66+ return self ._channel . is_connected
9667
9768 async def connect (self ) -> None :
9869 """Connect to the device using the appropriate protocol channel."""
9970 if self ._unsub :
10071 raise ValueError ("Already connected to the device" )
101- self ._unsub = await self ._v1_channel .subscribe (self ._on_message )
72+ self ._unsub = await self ._channel .subscribe (self ._on_message )
10273 _LOGGER .info ("Connected to V1 device %s" , self .name )
10374
10475 async def close (self ) -> None :
@@ -111,10 +82,7 @@ def _on_message(self, message: RoborockMessage) -> None:
11182 """Handle incoming messages from the device."""
11283 _LOGGER .debug ("Received message from device: %s" , message )
11384
114- async def get_status (self ) -> Status :
115- """Get the current status of the device.
116-
117- This is a placeholder command and will likely be changed/moved in the future.
118- """
119- status_type : type [Status ] = ModelStatus .get (self ._product_info .model , S7MaxVStatus )
120- return await self ._v1_channel .rpc_channel .send_command (RoborockCommand .GET_STATUS , response_type = status_type )
85+ @property
86+ def traits (self ) -> Mapping [str , Trait ]:
87+ """Return the traits of the device."""
88+ return MappingProxyType (self ._trait_map )
0 commit comments