Skip to content

Commit 3b70438

Browse files
committed
chore: Document combined mqtt channels
1 parent b9c5440 commit 3b70438

File tree

1 file changed

+108
-53
lines changed

1 file changed

+108
-53
lines changed

roborock/devices/README.md

Lines changed: 108 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ The devices module provides functionality to discover Roborock devices on the
44
network. This section documents the full lifecycle of device discovery across
55
Cloud 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

923
The 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
108148
4. **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)
111152
5. **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
112155
6. **Protocol Layer**: Message encoding/decoding for different device versions
113156
7. **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
320349
graph LR
@@ -430,18 +459,30 @@ The `Channel` abstraction provides a uniform interface for both MQTT and local c
430459

431460
This 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 = [
452493
rpc_channel = RpcChannel(strategies)
453494
```
454495

455-
For each command:
496+
For each V1 command:
456497
1. Try the first strategy (local)
457498
2. If it fails, try the next strategy (MQTT)
458499
3. 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

470525
Different device models use different protocol versions:

0 commit comments

Comments
 (0)