@@ -4,6 +4,20 @@ The devices module provides functionality to discover Roborock devices on the
44network. This section documents the full lifecycle of device discovery across
55Cloud and Network.
66
7+ ## Quick Start: Understanding Device Protocols
8+
9+ ** The library supports three device protocol versions, each with different capabilities:**
10+
11+ ### Protocol Summary
12+
13+ | Protocol | Device Examples | MQTT | Local TCP | Channel Type | Notes |
14+ | ----------| ----------------| ------| -----------| --------------| -------|
15+ | ** V1** (` pv=1.0 ` ) | Most vacuum robots (S7, S8, Q5, Q7, etc.) | ✅ | ✅ | ` V1Channel ` with ` RpcChannel ` | Prefers local, falls back to MQTT |
16+ | ** A01** (` pv=A01 ` ) | Dyad, Zeo washers | ✅ | ❌ | ` MqttChannel ` + helpers | MQTT only, DPS protocol |
17+ | ** B01** (` pv=B01 ` ) | Some newer models | ✅ | ❌ | ` MqttChannel ` + helpers | MQTT only, DPS protocol |
18+
19+ ** Key Point:** The ` DeviceManager ` automatically detects the protocol version and creates the appropriate channel type. You don't need to handle this manually.
20+
721## Architecture Overview
822
923The library is organized into distinct layers, each with a specific responsibility. ** Different device protocols use different channel implementations:**
@@ -35,14 +49,12 @@ graph TB
3549 A01C[A01 send_decoded_command<br/>MQTT only]
3650 B01C[B01 send_decoded_command<br/>MQTT only]
3751 RPC[RpcChannel<br/>Multi-strategy]
38- MC1[MqttChannel]
39- MC2[MqttChannel]
40- MC3[MqttChannel]
52+ MC[MqttChannel<br/>Per-device wrapper]
4153 LC[LocalChannel<br/>TCP :58867]
4254 end
4355
4456 subgraph "Session Layer"
45- MS[MqttSession<br/>Shared connection <br/>Idle timeout]
57+ MS[MqttSession<br/>SHARED by all devices <br/>Idle timeout]
4658 LS[LocalSession<br/>Factory]
4759 end
4860
@@ -72,18 +84,46 @@ graph TB
7284
7385 V1C --> RPC
7486 RPC -->|Strategy 1| LC
75- RPC -->|Strategy 2| MC1
76- A01C --> MC2
77- B01C --> MC3
87+ RPC -->|Strategy 2| MC
88+ A01C --> MC
89+ B01C --> MC
7890
79- MC1 --> MS
80- MC2 --> MS
81- MC3 --> MS
91+ MC --> MS
8292 LC --> LS
8393
84- MC1 --> V1P
85- MC2 --> A01P
86- MC3 --> B01P
94+ MC --> V1P
95+ MC --> A01P
96+ MC --> B01P
97+ LC --> V1P
98+
99+ MS --> MQTT
100+ LC --> TCP
101+ MQTT <--> TCP
102+
103+ style User fill:#e1f5ff
104+ style DM fill:#fff4e1
105+ style V1C fill:#ffe1e1
106+ style RPC fill:#ffe1e1
107+ style MS fill:#e1ffe1
108+ style V1P fill:#f0e1ff
109+ style A01P fill:#f0e1ff
110+ style B01P fill:#f0e1ff
111+ ```
112+
113+ ### Layer Responsibilities
114+
115+ 1 . ** Device Management Layer** : Detects protocol version (` pv ` field) and creates appropriate channels
116+ 2 . ** Device Types** : Different devices based on protocol version (V1, A01, B01)
117+ 3 . ** Traits Layer** : Protocol-specific device capabilities and commands
118+ 4 . ** Channel Layer** : Protocol-specific communication patterns
119+ - ** V1** : Full RPC channel with local + MQTT fallback
120+ - ** A01/B01** : Helper functions wrapping MqttChannel (MQTT only)
121+ - ** MqttChannel** : Per-device wrapper that uses shared ` MqttSession `
122+ 5 . ** Session Layer** : Connection pooling and subscription management
123+ - ** MqttSession** : ** Shared single connection** for all devices
124+ - ** LocalSession** : Factory for creating device-specific local connections
125+ 6 . ** Protocol Layer** : Message encoding/decoding for different device versions
126+ 7 . ** Transport Layer** : Low-level MQTT and TCP communication
87127 LC --> V1P
88128
89129 MS --> MQTT
@@ -108,10 +148,18 @@ graph TB
1081484. **Channel Layer**: Protocol-specific communication patterns
109149 - **V1**: Full RPC channel with local + MQTT fallback
110150 - **A01/B01**: Helper functions wrapping MqttChannel (MQTT only)
151+ - **MqttChannel**: Per-device wrapper (each device has its own `MqttChannel` instance)
1111525. **Session Layer**: Connection pooling and subscription management
153+ - **MqttSession**: **Single shared connection** for ALL devices (efficiency!)
154+ - **LocalSession**: Factory for creating device-specific local connections
1121556. **Protocol Layer**: Message encoding/decoding for different device versions
1131567. **Transport Layer**: Low-level MQTT and TCP communication
114157
158+ **Important:** All `MqttChannel` instances share the same `MqttSession`, which maintains a single MQTT connection to the broker. This means:
159+ - Only one TCP connection to the MQTT broker regardless of device count
160+ - Subscription management is centralized with idle timeout optimization
161+ - All devices communicate through device-specific MQTT topics on the shared connection
162+
115163### Protocol-Specific Architecture
116164
117165| Protocol | Channel Type | Local Support | RPC Strategy | Use Case |
@@ -273,48 +321,29 @@ sequenceDiagram
273321| ** Fallback** | Local → MQTT | N/A |
274322| ** Connection** | Requires network info fetch | Direct MQTT |
275323| ** Examples** | Most vacuum robots | Dyad washers, Zeo models |
276- MC->>RPC: decoded message
277- RPC-->>V1C: NetworkInfo
278-
279- Note over V1C: Connect locally using IP from NetworkInfo
280- V1C->>LC: connect()
281- LC->>Device: TCP Connect :58867
282- Device-->>LC: Connected
283-
284- Note over V1C: Send command (prefers local)
285- App->>V1C: send_command(GET_STATUS)
286- V1C->>RPC: send_command()
287- RPC->>LC: publish(request) [Try local first]
288- LC->>Device: Command
289- Device->>LC: Response
290- LC->>RPC: decoded message
291- RPC-->>App: Status
292- ```
293-
294-
295324
296- ### MQTT connection
325+ ### MQTT Connection (All Devices)
297326
298327- Initial device information must be obtained from MQTT
299- - We typically set up the MQTT device connection before the local device connection.
300- - The `NetworkingInfo` needs to be fetched to get additional information about connecting to the device:
301- - e.g. Local IP Address
328+ - For V1 devices, we set up the MQTT device connection before the local device connection
329+ - The ` NetworkingInfo ` needs to be fetched to get additional information about connecting to the device (e.g., Local IP Address)
302330 - This networking info can be cached to reduce network calls
303- - MQTT also is the only way to get the device Map
331+ - MQTT is also the only way to get the device Map
304332- Incoming and outgoing messages are decoded/encoded using the device ` local_key `
305- - Otherwise all commands may be performed locally.
333+ - For A01/B01 devices, MQTT is the only transport
306334
307- ### Local connection
335+ ### Local Connection (V1 Devices Only)
308336
309- - We can use the `ip` from the `NetworkingInfo` to find the device
310- - The local connection is preferred to for improved latency and reducing load on the cloud servers to avoid rate limiting.
337+ - We use the ` ip ` from the ` NetworkingInfo ` to find the device
338+ - The local connection is preferred for improved latency and reducing load on the cloud servers to avoid rate limiting
311339- Connections are made using a normal TCP socket on port ` 58867 `
312340- Incoming and outgoing messages are decoded/encoded using the device ` local_key `
313- - Messages received on the stream may be partially received so we keep a running buffer as messages are partially decoded
341+ - Messages received on the stream may be partially received, so we keep a running buffer as messages are partially decoded
342+ - ** Not available for A01/B01 devices**
314343
315- ### RPC Pattern
344+ ### RPC Pattern (V1 Devices)
316345
317- The library uses a publish/subscribe model for both MQTT and local connections, with an RPC abstraction on top:
346+ V1 devices use a publish/subscribe model for both MQTT and local connections, with an RPC abstraction on top:
318347
319348``` mermaid
320349graph LR
@@ -430,18 +459,30 @@ The `Channel` abstraction provides a uniform interface for both MQTT and local c
430459
431460This abstraction allows the RPC layer to work identically over both transports.
432461
433- #### MqttSession
462+ #### MqttSession (Shared Across All Devices)
434463
435- The ` MqttSession ` manages a shared MQTT connection for all devices:
464+ The ` MqttSession ` manages a ** single shared MQTT connection** for all devices:
436465
466+ - ** Single Connection** : Only one TCP connection to the MQTT broker, regardless of device count
467+ - ** Per-Device Topics** : Each device communicates via its own MQTT topics (e.g., ` rr/m/i/{user}/{username}/{duid} ` )
437468- ** Subscription Pooling** : Multiple callbacks can subscribe to the same topic
438- - ** Idle Timeout** : Keeps subscriptions alive for 10 seconds after the last callback unsubscribes
439- - ** Reconnection** : Automatically reconnects and re-establishes subscriptions on connection loss
469+ - ** Idle Timeout** : Keeps subscriptions alive for 10 seconds after the last callback unsubscribes (enables reuse during command bursts)
470+ - ** Reconnection** : Automatically reconnects and re-establishes all subscriptions on connection loss
440471- ** Thread-Safe** : Uses asyncio primitives for safe concurrent access
441472
442- #### RpcChannel with Multiple Strategies
473+ ** Efficiency ** : Creating 5 devices means 5 ` MqttChannel ` instances but only 1 ` MqttSession ` and 1 MQTT broker connection.
443474
444- The ` RpcChannel ` implements the request/response pattern over pub/sub channels:
475+ #### MqttChannel (Per-Device Wrapper)
476+
477+ Each device gets its own ` MqttChannel ` instance that:
478+ - Wraps the shared ` MqttSession `
479+ - Manages device-specific topics (publish to ` rr/m/i/.../duid ` , subscribe to ` rr/m/o/.../duid ` )
480+ - Handles protocol-specific encoding/decoding with the device's ` local_key `
481+ - Provides the same ` Channel ` interface as ` LocalChannel `
482+
483+ #### RpcChannel with Multiple Strategies (V1 Only)
484+
485+ The ` RpcChannel ` implements the request/response pattern over pub/sub channels and is ** only used by V1 devices** :
445486
446487``` python
447488# Example: V1Channel tries local first, then MQTT
@@ -452,19 +493,33 @@ strategies = [
452493rpc_channel = RpcChannel(strategies)
453494```
454495
455- For each command:
496+ For each V1 command:
4564971 . Try the first strategy (local)
4574982 . If it fails, try the next strategy (MQTT)
4584993 . Return the first successful result
459500
460- #### Health Management
501+ ** A01/B01 devices** don't use ` RpcChannel ` . Instead, they use helper functions (` send_decoded_command ` ) that directly wrap ` MqttChannel ` .
502+
503+ #### Protocol-Specific Channel Architecture
461504
462- Each strategy can have a ` HealthManager ` that tracks success/failure:
505+ | Component | V1 Devices | A01/B01 Devices |
506+ | -----------| ------------| -----------------|
507+ | ** Channel Class** | ` V1Channel ` | ` MqttChannel ` directly |
508+ | ** RPC Abstraction** | ` RpcChannel ` with strategies | Helper functions |
509+ | ** Strategy Pattern** | ✅ Multi-strategy (Local → MQTT) | ❌ Direct MQTT only |
510+ | ** Health Manager** | ✅ Tracks local/MQTT health | ❌ Not needed |
511+ | ** Code Location** | ` v1_channel.py ` | ` a01_channel.py ` , ` b01_channel.py ` |
512+
513+ #### Health Management (V1 Only)
514+
515+ Each V1 RPC strategy can have a ` HealthManager ` that tracks success/failure:
463516
464517- ** Exponential Backoff** : After failures, wait before retrying
465518- ** Automatic Recovery** : Periodically attempt to restore failed connections
466519- ** Network Info Refresh** : Refresh local IP addresses after extended periods
467520
521+ A01/B01 devices don't need health management since they only use MQTT (no fallback).
522+
468523### Protocol Versions
469524
470525Different device models use different protocol versions:
0 commit comments