roombapy-prime 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roombapy_prime-0.3.1/LICENSE +21 -0
- roombapy_prime-0.3.1/PKG-INFO +560 -0
- roombapy_prime-0.3.1/README.md +526 -0
- roombapy_prime-0.3.1/pyproject.toml +83 -0
- roombapy_prime-0.3.1/roombapy_prime/__init__.py +95 -0
- roombapy_prime-0.3.1/roombapy_prime/auth.py +1002 -0
- roombapy_prime-0.3.1/roombapy_prime/aws_sigv4.py +126 -0
- roombapy_prime-0.3.1/roombapy_prime/diagnostics.py +1097 -0
- roombapy_prime-0.3.1/roombapy_prime/ids.py +96 -0
- roombapy_prime-0.3.1/roombapy_prime/models/__init__.py +38 -0
- roombapy_prime-0.3.1/roombapy_prime/models/enums_common.py +117 -0
- roombapy_prime-0.3.1/roombapy_prime/models/favorites.py +146 -0
- roombapy_prime-0.3.1/roombapy_prime/models/geometry.py +87 -0
- roombapy_prime-0.3.1/roombapy_prime/models/livemap.py +392 -0
- roombapy_prime-0.3.1/roombapy_prime/models/map_bundle.py +1156 -0
- roombapy_prime-0.3.1/roombapy_prime/models/map_editing.py +1137 -0
- roombapy_prime-0.3.1/roombapy_prime/models/mission_control.py +1581 -0
- roombapy_prime-0.3.1/roombapy_prime/models/mission_history.py +1456 -0
- roombapy_prime-0.3.1/roombapy_prime/models/robot_info.py +3982 -0
- roombapy_prime-0.3.1/roombapy_prime/models/schedules_dnd.py +671 -0
- roombapy_prime-0.3.1/roombapy_prime/models/time_estimates.py +262 -0
- roombapy_prime-0.3.1/roombapy_prime/mqtt_client.py +1608 -0
- roombapy_prime-0.3.1/roombapy_prime/prime_factory.py +111 -0
- roombapy_prime-0.3.1/roombapy_prime/prime_robot.py +1985 -0
- roombapy_prime-0.3.1/roombapy_prime/py.typed +0 -0
- roombapy_prime-0.3.1/roombapy_prime/rest_client.py +1812 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/conftest.py +28 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_auth.py +1207 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_aws_sigv4.py +99 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_diagnostics.py +793 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_favorites.py +117 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_ids.py +112 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_init.py +80 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_livemap.py +54 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_map_bundle.py +89 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_map_editing.py +292 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_models.py +7352 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_mqtt_client.py +1700 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_prime_factory.py +175 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_prime_robot.py +2431 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_rest_client.py +1579 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_schedules_dnd.py +59 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_time_estimates.py +241 -0
- roombapy_prime-0.3.1/roombapy_prime/tests/test_tools_boundary.py +67 -0
- roombapy_prime-0.3.1/roombapy_prime/vendor_errors.py +3057 -0
- roombapy_prime-0.3.1/roombapy_prime/vendor_reference.json +7838 -0
- roombapy_prime-0.3.1/roombapy_prime/vendor_reference.py +106 -0
- roombapy_prime-0.3.1/roombapy_prime.egg-info/PKG-INFO +560 -0
- roombapy_prime-0.3.1/roombapy_prime.egg-info/SOURCES.txt +51 -0
- roombapy_prime-0.3.1/roombapy_prime.egg-info/dependency_links.txt +1 -0
- roombapy_prime-0.3.1/roombapy_prime.egg-info/requires.txt +14 -0
- roombapy_prime-0.3.1/roombapy_prime.egg-info/top_level.txt +1 -0
- roombapy_prime-0.3.1/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jean-Christoph (@johnnyh1975)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roombapy-prime
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Cloud client for iRobot 'Prime'/V4-generation robots — login, MQTT shadow, region cleaning, maps and schedules, confirmed against real hardware
|
|
5
|
+
Author: Jean-Christoph
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/johnnyh1975/roombapy-prime
|
|
8
|
+
Project-URL: Repository, https://github.com/johnnyh1975/roombapy-prime
|
|
9
|
+
Project-URL: Issues, https://github.com/johnnyh1975/roombapy-prime/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/johnnyh1975/roombapy-prime/blob/main/CHANGELOG.md
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Home Automation
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: aiohttp>=3.9
|
|
23
|
+
Requires-Dist: paho-mqtt<3.0,>=2.0
|
|
24
|
+
Requires-Dist: certifi
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == "test"
|
|
28
|
+
Requires-Dist: pytest-cov>=5.0; extra == "test"
|
|
29
|
+
Provides-Extra: map
|
|
30
|
+
Requires-Dist: Pillow>=10.0; extra == "map"
|
|
31
|
+
Provides-Extra: tools
|
|
32
|
+
Requires-Dist: roombapy-prime-tools>=0.3.1; extra == "tools"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# roombapy-prime
|
|
36
|
+
|
|
37
|
+
[](https://github.com/johnnyh1975/roombapy-prime/actions/workflows/ci.yml)
|
|
38
|
+
|
|
39
|
+
An independent, async Python client library for iRobot's cloud-connected
|
|
40
|
+
**"Prime"/V4-generation** robots — the successor line to the Classic
|
|
41
|
+
protocol devices supported by [roombapy](https://github.com/pschmitt/roombapy).
|
|
42
|
+
|
|
43
|
+
> **Status: v0.3.1.** (currently `0.3.1`) Reading and writing both work
|
|
44
|
+
> against real hardware, confirmed across a dozen field testers' accounts:
|
|
45
|
+
> login, MQTT, mission control, schedules, map edits, favorites, robot
|
|
46
|
+
> settings, and **region-based cleaning** — sending a robot to specific
|
|
47
|
+
> rooms, from a saved favorite, built from scratch, and with the *scope*
|
|
48
|
+
> confirmed rather than merely the delivery: a single-room command on a
|
|
49
|
+
> Combo 105 covered 234 sq ft against two whole-house runs at 644.
|
|
50
|
+
>
|
|
51
|
+
> **Splitting and merging rooms** are confirmed on hardware too. The
|
|
52
|
+
> merge command's wire name is `arrange_room`, not `merge_rooms` — a
|
|
53
|
+
> discriminator that contradicts its own class name, attested now in app
|
|
54
|
+
> bytecode, on a live robot, and in the robot's own firmware.
|
|
55
|
+
>
|
|
56
|
+
> Virtual wall and keep-out zone writes **work**, on two independent
|
|
57
|
+
> accounts, including the write / re-read / write round trip that
|
|
58
|
+
> separates "accepted" from "stored". The HTTP 500 this section used to
|
|
59
|
+
> describe was solved: `virwall` starts with a COUNT of the walls.
|
|
60
|
+
>
|
|
61
|
+
> One thing has still never been tried: a write carrying a **modified**
|
|
62
|
+
> list. Every confirmed write resent zones unchanged. See
|
|
63
|
+
> [Confidence & known gaps](#confidence--known-gaps).
|
|
64
|
+
>
|
|
65
|
+
> The diagnostic scripts live in a **separate distribution**
|
|
66
|
+
> ([`tools/`](tools/README.md)) so that installing this library never puts
|
|
67
|
+
> robot-moving commands on your PATH.
|
|
68
|
+
|
|
69
|
+
## Contents
|
|
70
|
+
|
|
71
|
+
- [Features](#features)
|
|
72
|
+
- [Installation](#installation)
|
|
73
|
+
- [Quick start](#quick-start)
|
|
74
|
+
- [Testing](#testing)
|
|
75
|
+
- [Contributing](#contributing)
|
|
76
|
+
- [Confidence & known gaps](#confidence--known-gaps)
|
|
77
|
+
- [What the vendor's own app told us](#what-the-vendors-own-app-told-us)
|
|
78
|
+
- [Data privacy & security](#data-privacy--security)
|
|
79
|
+
- [Why not just extend roombapy?](#why-not-just-extend-roombapy)
|
|
80
|
+
- [Documentation](#documentation)
|
|
81
|
+
- [Credits](#credits)
|
|
82
|
+
- [License](#license)
|
|
83
|
+
|
|
84
|
+
## Features
|
|
85
|
+
|
|
86
|
+
- **Login & session** — account login (Gigya + AWS Custom Authorizer), automatic MQTT token refresh
|
|
87
|
+
- **Live state** — current robot status, one-shot (`get_state()`) or continuous (`watch_state()`); battery percentage and charging/dock state are confirmed live via the named shadow `ro-currentstate` (`CurrentStateShadow`) — a separate, older `RobotStatusV2` parser also exists but is unconfirmed to appear anywhere (see the confidence table)
|
|
88
|
+
- **Mission control** — start/stop/pause/resume/dock via `send_simple_command()`, confirmed working live against a real robot; the richer, region-aware `send_mission_command()` remains available but is now believed incorrect for basic use
|
|
89
|
+
- **Favorites** — list, create, update, delete, reorder saved cleaning routines
|
|
90
|
+
- **Maps** — read map metadata and active versions, edit rooms/zones/furniture/virtual walls, watch the live map while cleaning, download+unpack the full map bundle
|
|
91
|
+
- **Schedules** — recurring cleaning schedules per household (list, create, update, delete)
|
|
92
|
+
- **Mission history** — past cleaning runs with duration, coverage, and end reason
|
|
93
|
+
- **Parts & device info** — consumable part status, reset after replacement, serial number data, time estimates, notification feed. Find-my-robot: **confirmed working** via `send_simple_command("find")` (jayjay) — a genuine, audible chime with no robot movement; two other mechanisms (a REST endpoint, a shadow write) were tried first and confirmed **not working** — see the docstrings on `poll_echo_value()`/`trigger_echo_via_shadow()`/`send_simple_command()`
|
|
94
|
+
- **Settings** — Do Not Disturb windows, cleaning profiles, per-map default routine suggestions
|
|
95
|
+
- **Diagnostics** — a companion distribution, [`roombapy-prime-tools`](tools/README.md), validates all of the above against a real account and reports what works. Deliberately separate: several of its commands move a real robot, and they have no business on the PATH of a Home Assistant installation that only consumes this library.
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install roombapy-prime
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
This gives you the **library only** — no console scripts at all. That is
|
|
104
|
+
deliberate.
|
|
105
|
+
|
|
106
|
+
**If you want the diagnostic tools** (to test your own robot, or to help
|
|
107
|
+
with the open questions below), install those instead — they pull this
|
|
108
|
+
library in as a dependency, so it stays one command:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install roombapy-prime-tools
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Upgrading, if you have the tools
|
|
115
|
+
|
|
116
|
+
**Upgrade the tools, not the library.** They are two distributions, and
|
|
117
|
+
upgrading the library on its own leaves the tools where they were:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# right -- brings the matching library with it
|
|
121
|
+
pip install --upgrade roombapy-prime-tools
|
|
122
|
+
|
|
123
|
+
# wrong, if you have the tools -- upgrades half of the pair
|
|
124
|
+
pip install --upgrade roombapy-prime
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This is not theoretical. @chairstacker upgraded the library to b6, ran
|
|
128
|
+
`verify-writes custom_initiator`, and was asked for a cleaning-history
|
|
129
|
+
entry — an instruction b6 had removed, because `find` creates no history
|
|
130
|
+
entry. He reported the missing entry as a finding. The tool was still b5.
|
|
131
|
+
|
|
132
|
+
`verify-writes` now says so at startup when the two disagree, and names
|
|
133
|
+
the command above. That is a backstop rather than a fix: the reason the
|
|
134
|
+
two can drift is that the tools are deliberately a separate
|
|
135
|
+
distribution, so several commands that move a real robot stay off the
|
|
136
|
+
PATH of a Home Assistant installation that only consumes the library.
|
|
137
|
+
|
|
138
|
+
See [`tools/README.md`](tools/README.md) for what they do and how to use
|
|
139
|
+
them safely.
|
|
140
|
+
|
|
141
|
+
Requires Python 3.11+. Dependencies: `aiohttp`, `paho-mqtt`, `certifi`.
|
|
142
|
+
|
|
143
|
+
## Quick start
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import asyncio
|
|
147
|
+
import aiohttp
|
|
148
|
+
from roombapy_prime import PrimeFactory
|
|
149
|
+
|
|
150
|
+
async def main():
|
|
151
|
+
async with aiohttp.ClientSession() as session:
|
|
152
|
+
robot = await PrimeFactory.create_prime_robot(
|
|
153
|
+
session=session,
|
|
154
|
+
username="you@example.com",
|
|
155
|
+
password="hunter2",
|
|
156
|
+
country_code="US",
|
|
157
|
+
# blid="BLID123", # optional — first robot on the account is used otherwise
|
|
158
|
+
)
|
|
159
|
+
await robot.connect()
|
|
160
|
+
|
|
161
|
+
state = await robot.get_state()
|
|
162
|
+
print(state.payload)
|
|
163
|
+
|
|
164
|
+
async for delta in robot.watch_state(): # runs until cancelled
|
|
165
|
+
print(delta.payload)
|
|
166
|
+
|
|
167
|
+
asyncio.run(main())
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A few other things you can do with the same `robot` object, once connected:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
favorites = await robot.get_favorites()
|
|
174
|
+
history = await robot.get_mission_history(robot.blid, max_reports=10)
|
|
175
|
+
maps = await robot.get_active_map_versions()
|
|
176
|
+
|
|
177
|
+
# Sends a real command to the robot — confirmed working live (see the
|
|
178
|
+
# status note above), but it still moves your actual robot.
|
|
179
|
+
await robot.send_simple_command("start") # or "stop"/"pause"/"resume"/"dock"
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
There's more — schedules, DND settings, map editing, live map streaming.
|
|
183
|
+
See [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md) for every method
|
|
184
|
+
and model organized by feature area, with confidence markers per item —
|
|
185
|
+
or the module docstrings in `roombapy_prime/` directly for the full
|
|
186
|
+
evidence behind each one.
|
|
187
|
+
|
|
188
|
+
Eleven runnable examples are in [`examples/`](examples/). Each reads
|
|
189
|
+
credentials from environment variables; none hardcode a password, and
|
|
190
|
+
every one that writes anything puts it behind a flag.
|
|
191
|
+
|
|
192
|
+
| Example | Covers |
|
|
193
|
+
|---|---|
|
|
194
|
+
| `basic_usage.py` | Log in, connect, read state, watch for updates |
|
|
195
|
+
| `clean_regions.py` | Send the robot to named rooms and zones |
|
|
196
|
+
| `mission_control.py` | Start, pause, resume, dock |
|
|
197
|
+
| `schedules.py` | Reading and writing cleaning schedules |
|
|
198
|
+
| `favorites_and_history.py` | Saved favourites and past missions |
|
|
199
|
+
| `settings.py` | Robot settings |
|
|
200
|
+
| `maps.py` | Map versions, region names, downloading the bundle |
|
|
201
|
+
| `maintenance.py` | Consumable part counters, and resetting them |
|
|
202
|
+
| `do_not_disturb.py` | Quiet hours — two mutually exclusive shapes |
|
|
203
|
+
| `watching.py` | Live position and dock streams instead of polling |
|
|
204
|
+
| `error_handling.py` | Which failures are worth retrying, and which are not |
|
|
205
|
+
|
|
206
|
+
If you are writing anything that logs in unattended, start with
|
|
207
|
+
`error_handling.py`: an `AuthCredentialsError` will never succeed on
|
|
208
|
+
retry, and a naive retry loop turns it into `AuthRateLimitedError`.
|
|
209
|
+
|
|
210
|
+
## Testing
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install -e ".[test]"
|
|
214
|
+
pytest roombapy_prime/tests/
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
1057+ tests for the library, plus 462 for the command-line tools —
|
|
218
|
+
structural checks against decompiled source,
|
|
219
|
+
a byte-for-byte regression pin for the SigV4 signer, genuine
|
|
220
|
+
multi-threading tests for the connection lock, and more. This validates
|
|
221
|
+
internal consistency (the library builds the requests it claims to
|
|
222
|
+
build); it does **not** validate that a real server accepts them — only
|
|
223
|
+
the diagnostics script below can do that. See
|
|
224
|
+
[`docs/internal/DEVELOPMENT_NOTES.md`](docs/internal/DEVELOPMENT_NOTES.md) for the
|
|
225
|
+
detailed breakdown (German; all code, comments, and this README are in
|
|
226
|
+
English per project convention).
|
|
227
|
+
|
|
228
|
+
## Contributing
|
|
229
|
+
|
|
230
|
+
If you own a Prime/V4 robot, running the diagnostics against your own
|
|
231
|
+
account is by far the most useful thing you can do. Every "confirmed"
|
|
232
|
+
entry above exists because somebody did exactly that.
|
|
233
|
+
|
|
234
|
+
Three findings that shaped this library came from testers pasting their
|
|
235
|
+
**full** terminal output rather than summarising it as "didn't work":
|
|
236
|
+
the live map turning out to be zlib-compressed (visible in the first two
|
|
237
|
+
bytes of a diagnostic line), `initiator` being mandatory for region
|
|
238
|
+
commands, and a robot's own capability list revealing five fields this
|
|
239
|
+
library was silently discarding. None of those would have surfaced from
|
|
240
|
+
a description of the symptom.
|
|
241
|
+
|
|
242
|
+
The most useful things right now:
|
|
243
|
+
|
|
244
|
+
- **Virtual wall writes with a CHANGED list** — never attempted. Writes
|
|
245
|
+
themselves are confirmed on two accounts (@chairstacker resent four
|
|
246
|
+
zones of two types; @jayjay13011 wrote, re-read the new map version
|
|
247
|
+
and wrote again), but every one of them resent the existing zones
|
|
248
|
+
unchanged. Adding, moving or removing a zone is untested, and
|
|
249
|
+
`set_virtual_wall` **replaces the whole shared list** — a partial list
|
|
250
|
+
deletes everything omitted.
|
|
251
|
+
- **Robot settings other than child lock** — they write and read back
|
|
252
|
+
cleanly; whether they change anything is untested.
|
|
253
|
+
- **Anything at all on hardware not listed above.** The capability set
|
|
254
|
+
genuinely differs between models, and each new device has so far
|
|
255
|
+
turned up something.
|
|
256
|
+
|
|
257
|
+
The tools are a **separate distribution** — one command, and it pulls
|
|
258
|
+
this library in with it:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
pip install roombapy-prime-tools
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Start with `roombapy-prime-validate`: read-only, sends nothing, and its
|
|
265
|
+
output alone answers several open questions.
|
|
266
|
+
|
|
267
|
+
> If `roombapy-prime-validate` is not found, you have the library
|
|
268
|
+
> installed but not the tools — the two commands above are different
|
|
269
|
+
> packages. `python -m roombapy_prime.diagnostics` runs the same thing
|
|
270
|
+
> from the library alone. Full setup, the staged
|
|
271
|
+
safety model, and what each script does:
|
|
272
|
+
**[`tools/README.md`](tools/README.md)**.
|
|
273
|
+
|
|
274
|
+
Bug reports and findings are welcome even without a robot — the
|
|
275
|
+
[evidence trail](docs/internal/EVIDENCE_TRAIL.md) documents how each
|
|
276
|
+
conclusion was reached, including the ones that turned out wrong, and a
|
|
277
|
+
second pair of eyes on that reasoning is genuinely useful.
|
|
278
|
+
|
|
279
|
+
## Confidence & known gaps
|
|
280
|
+
|
|
281
|
+
The honest version. "Confirmed" below means a real person watched a real
|
|
282
|
+
robot and reported back — not that a request returned without an error.
|
|
283
|
+
|
|
284
|
+
**Summary:** reading works. Writing works, with one exception noted
|
|
285
|
+
below. Three independent accounts have exercised this, on a Roomba Plus
|
|
286
|
+
505 Combo, a Roomba Combo (G18-series) and a Y41-series machine.
|
|
287
|
+
|
|
288
|
+
### Confirmed on real hardware
|
|
289
|
+
|
|
290
|
+
| Area | How it was confirmed |
|
|
291
|
+
|---|---|
|
|
292
|
+
| Login (Gigya + AWS Custom Authorizer), token refresh | multiple accounts |
|
|
293
|
+
| MQTT connection, named-shadow reads | multiple accounts |
|
|
294
|
+
| Reading state, favorites, mission history, maps, schedules, parts | multiple accounts |
|
|
295
|
+
| Mission control — `start`/`stop`/`pause`/`resume`/`dock` | robot visibly reacted |
|
|
296
|
+
| `find` (audible locate, no movement) | robot chimed |
|
|
297
|
+
| **Region cleaning from a saved favorite** | robot cleaned the named rooms |
|
|
298
|
+
| **Region cleaning built from scratch** | robot travelled to room 12 and cleaned it |
|
|
299
|
+
| Schedule writes — unchanged resend and a real disable | change took effect |
|
|
300
|
+
| **Map editing (`edit_map`, room rename)** | renamed and reverted, both confirmed in the app |
|
|
301
|
+
| Zone names in the map bundle | **not established.** The reading code had a typo that made it return nothing on every bundle, so the earlier "confirmed" here rested on a search that never ran. Fixed in b15 and now genuinely untested. |
|
|
302
|
+
| Map editing — room rename, with revert | twice, name changed in the app |
|
|
303
|
+
| Favorite writes — resend, colour change, delete | change visible in the app |
|
|
304
|
+
| Robot settings — child lock | appeared in the app, robot announced it audibly |
|
|
305
|
+
| Keep-out zone / no-mop zone **reads** | two real zones, both types correctly identified |
|
|
306
|
+
| **Splitting and merging rooms** | both accepted on a Combo 105, map re-rendered (response level; geometry not audited) |
|
|
307
|
+
| **Region cleaning on an x05** | 234 sq ft against two whole-house runs at 644 — the area is the proof, not the acknowledgement |
|
|
308
|
+
| Firmware catalogue (`get_firmware`) | a real response, parsed |
|
|
309
|
+
| **Dock report topics** | `dock/paddry/report` received live; firmware confirms the family is exactly {evac, refill, padwash, paddry} |
|
|
310
|
+
| **The local channel still answers** | the APP dropped local networking in 3.0.0; the robots did not |
|
|
311
|
+
|
|
312
|
+
### Independently reconstructed
|
|
313
|
+
|
|
314
|
+
`samm-git/irobot-explore` rebuilt the same protocol from a different app
|
|
315
|
+
version (1.6.0) on different hardware, with no knowledge of this
|
|
316
|
+
project. Where two independent derivations agree, a thing is confirmed
|
|
317
|
+
rather than one derivation deep.
|
|
318
|
+
|
|
319
|
+
Agreeing without contradiction: service discovery, Gigya login,
|
|
320
|
+
`/v2/login`, the four IoT session attributes, the custom-authorizer
|
|
321
|
+
flow, shadow topics and envelope, the `cmd` topic with `p2map_id` and
|
|
322
|
+
`regions`, `rid`/`zid`/`tid`, settings under `desired`, SigV4 for map
|
|
323
|
+
data.
|
|
324
|
+
|
|
325
|
+
Where we differed, that reconstruction was right twice — see the
|
|
326
|
+
0.3.0 changelog entry.
|
|
327
|
+
|
|
328
|
+
### Region cleaning: what it took, and what it needs
|
|
329
|
+
|
|
330
|
+
This was the project's central unknown for months. Two things were
|
|
331
|
+
required, and neither is obvious:
|
|
332
|
+
|
|
333
|
+
- **`initiator` is mandatory.** A stored favorite does not carry one —
|
|
334
|
+
the app adds it at send time. Resending a favorite unchanged is
|
|
335
|
+
accepted, acknowledged, and silently ignored.
|
|
336
|
+
- **The wire keys are `start` and `region_id`**, not `clean` and `id`.
|
|
337
|
+
The latter pair was an assumption recorded in this project's own code
|
|
338
|
+
and never checked.
|
|
339
|
+
|
|
340
|
+
A map version is **not** required. The robot re-versions its map every
|
|
341
|
+
few seconds while cleaning (five values inside 37 seconds in one real
|
|
342
|
+
capture), so a stored favorite is stale within a minute of being saved —
|
|
343
|
+
and commands carrying versions hours out of date started missions
|
|
344
|
+
regardless.
|
|
345
|
+
|
|
346
|
+
### Known broken
|
|
347
|
+
|
|
348
|
+
- **A virtual wall write carrying a CHANGED list** — never attempted.
|
|
349
|
+
The HTTP 500 that used to sit here was solved: `virwall` starts with a
|
|
350
|
+
COUNT of the walls. Writes are confirmed on two accounts, including
|
|
351
|
+
the write / re-read / write round trip that separates "accepted" from
|
|
352
|
+
"stored" (#28, closed 30 July).
|
|
353
|
+
But every confirmed write **resent the existing zones unchanged**.
|
|
354
|
+
Adding, moving or removing one is untested, and `set_virtual_wall`
|
|
355
|
+
replaces the whole shared list — a partial list deletes everything
|
|
356
|
+
omitted. That hazard is guarded by a test, not by the server.
|
|
357
|
+
|
|
358
|
+
- **`schedHold`** writes succeed and read back correctly, and the
|
|
359
|
+
schedule stays active in the app. Writing it to `rw-settings` is
|
|
360
|
+
evidently not the mechanism the app uses.
|
|
361
|
+
|
|
362
|
+
Worth knowing how that surfaced: this project's cross-check against the
|
|
363
|
+
classic shadow flagged the divergence *before* the tester looked in the
|
|
364
|
+
app. Two sources disagreeing turned out to mean "the write did not
|
|
365
|
+
take", which makes that check a real signal.
|
|
366
|
+
|
|
367
|
+
### Untested
|
|
368
|
+
|
|
369
|
+
- **Robot settings other than child lock** — `ecoCharge`, `noAutoPasses`
|
|
370
|
+
and `vacHigh` all write and read back cleanly; none has an easily
|
|
371
|
+
observable effect, so their real-world behaviour is unknown.
|
|
372
|
+
- **Multi-robot household and teaming** concepts beyond basic settings
|
|
373
|
+
scoping.
|
|
374
|
+
- The discriminator value inside a map-edit command's `edit_cmd`
|
|
375
|
+
envelope. The envelope shape and 8 of 9 commands' fields are confirmed;
|
|
376
|
+
`SetRoomMetadata` and `VirtualWall` use custom serializers whose
|
|
377
|
+
internals are not.
|
|
378
|
+
- **Whole-house cleaning through the region command path** (`clean_all` /
|
|
379
|
+
`select_all`). Deliberately untested rather than assumed: a wrong guess
|
|
380
|
+
cleans the whole house. Reading firmware 3.8.126 narrowed it — there is
|
|
381
|
+
no `clean_all` field at all, and scope comes from whether `regions` is
|
|
382
|
+
present — but that is architecture-consistent inference, not a proven
|
|
383
|
+
branch condition. The safe rule is unchanged: send a global command
|
|
384
|
+
only when whole-house is positively intended, and never rely on
|
|
385
|
+
`command_type=START` to limit scope. START is the operating mode, not a
|
|
386
|
+
scope limiter.
|
|
387
|
+
- **Uploading a p2map** back to the robot, and the services write path.
|
|
388
|
+
Both are marked at their call sites too.
|
|
389
|
+
|
|
390
|
+
### A warning if you search for help
|
|
391
|
+
|
|
392
|
+
Every public example of Roomba region cleaning you will find is for the
|
|
393
|
+
**Classic** protocol: `pmap_id`, `user_pmapv_id`, a flat payload, local
|
|
394
|
+
MQTT. Prime/V4 uses `p2map_id` and a different command structure
|
|
395
|
+
entirely. The names are close enough to look applicable and are not.
|
|
396
|
+
|
|
397
|
+
The full reasoning behind every entry above — including the conclusions
|
|
398
|
+
that turned out wrong and why — is in
|
|
399
|
+
[`docs/internal/EVIDENCE_TRAIL.md`](docs/internal/EVIDENCE_TRAIL.md).
|
|
400
|
+
|
|
401
|
+
## What the vendor's own app told us
|
|
402
|
+
|
|
403
|
+
`com.irobot.home.prime` 3.0.0 is a Flutter rewrite, and its data layer
|
|
404
|
+
ships as plain Kotlin serialisers rather than compiled constants. A
|
|
405
|
+
systematic comparison against it — 223 serialiser classes, 87 enums, 71
|
|
406
|
+
request classes, 25 locale files — corrected this library in places no
|
|
407
|
+
amount of field testing would have found, because **nothing was
|
|
408
|
+
failing**:
|
|
409
|
+
|
|
410
|
+
| Found | Why it mattered |
|
|
411
|
+
|---|---|
|
|
412
|
+
| `dirt`, `map_id`, `covStrat` | read here as `numberOfDirtDetects`, `staticMapId`, `coverageStrategy` — plausible names no robot has ever sent, so all three read `None` on every mission ever recorded |
|
|
413
|
+
| `cmd`, `disc`, `poly`, `tentativeLoc` | four timeline event types dropped from every real timeline, because this library read the long forms |
|
|
414
|
+
| `coverage` | per-room mission progress, declared beside fields already read. `RoomEvent`'s docstring spent fourteen lines reasoning about what `area` and `total_area` mean; the field that answers it was in the same object |
|
|
415
|
+
| 112 error codes | with iRobot's own title and explanation in 25 languages. Of 126 labels written here, **two** matched the vendor's |
|
|
416
|
+
| `schedule_id` inside options | 3.0.0 moved it; a schedule whose id cannot be found is one nobody can edit, and that reads as an empty calendar rather than an error |
|
|
417
|
+
| 24 writable settings | `audio.volume` with a dot, not `audio`; `padWetness.padPlate` addressed directly, retiring a read-modify-write recommendation |
|
|
418
|
+
|
|
419
|
+
The full comparison — what was checked, what was corrected, what was deliberately left alone, and
|
|
420
|
+
what the APK cannot answer — is in
|
|
421
|
+
**[docs/internal/APK_3_0_0_FINDINGS.md](docs/internal/APK_3_0_0_FINDINGS.md)**.
|
|
422
|
+
|
|
423
|
+
**And one thing it did not settle.** The app spells four commands in
|
|
424
|
+
camelCase (`washPad`, `dryPad`) where this library uses lowercase. The
|
|
425
|
+
lowercase forms are what a real robot recorded in its own shadow, with a
|
|
426
|
+
pad-wash counter to match, so they stay. A confirmed shape outranks a
|
|
427
|
+
plausible one — a rule this comparison had cause to apply three times.
|
|
428
|
+
|
|
429
|
+
## Does your robot still answer locally?
|
|
430
|
+
|
|
431
|
+
**Yes — confirmed on current firmware.**
|
|
432
|
+
|
|
433
|
+
App 2.2.4 carried a complete local API — 46 local-socket serializers,
|
|
434
|
+
`irobotmcs` discovery, port 5678. App 3.0.0 has none of it. This
|
|
435
|
+
library previously concluded from that the local path had been
|
|
436
|
+
*removed*, which was wrong: the **app** stopped using it, the robots
|
|
437
|
+
did not. An app dropping a path says nothing about the firmware behind
|
|
438
|
+
it.
|
|
439
|
+
|
|
440
|
+
Two independent confirmations on `p25-705+9.3.6+I3.8.149`, current as
|
|
441
|
+
of August 2026: a field tester's discovery run (SKU W155020) and the
|
|
442
|
+
author of `samm-git/irobot-explore`, who speaks the channel live.
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
roombapy-prime-verify-local-channel
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Four stages — UDP discovery, TCP connect, TLS handshake, and
|
|
449
|
+
deliberately **no** MQTT CONNECT. No credentials, no cloud, nothing sent
|
|
450
|
+
to the robot beyond the nine-byte discovery broadcast.
|
|
451
|
+
|
|
452
|
+
### Where it stands
|
|
453
|
+
|
|
454
|
+
| stage | on current firmware |
|
|
455
|
+
|---|---|
|
|
456
|
+
| UDP discovery | **answers** |
|
|
457
|
+
| TCP :8883 | **open** |
|
|
458
|
+
| TLS handshake | **fails**, `BAD_SIGNATURE` |
|
|
459
|
+
|
|
460
|
+
The TLS failure is the interesting part. The robot signs with a key
|
|
461
|
+
that does not match the certificate it presents. TLS 1.3 carries that
|
|
462
|
+
signature in `CertificateVerify`; TLS 1.2 with an ECDHE suite carries
|
|
463
|
+
it in `ServerKeyExchange`. Capping the version changes *which message*
|
|
464
|
+
holds the bad signature, not whether one is sent — a hypothesis this
|
|
465
|
+
project held and a field run disproved.
|
|
466
|
+
|
|
467
|
+
One case remains untested: a **static RSA** suite has no server
|
|
468
|
+
signature at all. If the robot's legacy stack offers one, nothing is
|
|
469
|
+
signed for a client to reject. The tool now tries it as a third
|
|
470
|
+
attempt.
|
|
471
|
+
|
|
472
|
+
So a native helper with a patched TLS library is the only route anyone
|
|
473
|
+
has **got working** — not, as this README previously implied, the only
|
|
474
|
+
route that could exist. Nobody has yet demonstrated that pure Python
|
|
475
|
+
cannot do it.
|
|
476
|
+
|
|
477
|
+
### Two caveats worth stating plainly
|
|
478
|
+
|
|
479
|
+
**The channel is closed until something opens it.** It comes up as part
|
|
480
|
+
of the BLE provisioning flow and closes again on reboot, so a silent
|
|
481
|
+
run means "nobody has provisioned this robot recently", not "the
|
|
482
|
+
firmware dropped it".
|
|
483
|
+
|
|
484
|
+
**A local transport removes the round trip, not the dependency.** The
|
|
485
|
+
reference implementation that speaks this channel still logs in to the
|
|
486
|
+
cloud once, to fetch the robot's local password.
|
|
487
|
+
|
|
488
|
+
## Data privacy & security
|
|
489
|
+
|
|
490
|
+
**In one sentence:** everything goes directly to iRobot's own cloud
|
|
491
|
+
infrastructure, nothing is sent to any third party, and nothing is
|
|
492
|
+
written to disk by this library unless you explicitly ask for it.
|
|
493
|
+
|
|
494
|
+
- [`docs/DATA_PRIVACY.md`](docs/DATA_PRIVACY.md) — what data goes
|
|
495
|
+
where, and what this library does and doesn't store, verified
|
|
496
|
+
directly against the code
|
|
497
|
+
- [`SECURITY.md`](SECURITY.md) — credential handling, TLS
|
|
498
|
+
verification, and what's still unverified from a security standpoint
|
|
499
|
+
|
|
500
|
+
## Why not just extend roombapy?
|
|
501
|
+
|
|
502
|
+
Classic-protocol robots talk local MQTT with `ssl.CERT_NONE` and a
|
|
503
|
+
blid/password pair — no account, no internet round-trip. Prime/V4 robots
|
|
504
|
+
are cloud-only: AWS IoT Custom Authorizer sessions, request/response
|
|
505
|
+
"shadow" state instead of a local firehose, and a REST API for map
|
|
506
|
+
management that Classic doesn't have at all. Different trust model,
|
|
507
|
+
different protocol shape, not just a missing feature — see
|
|
508
|
+
[`docs/internal/ROOMBAPY_COMPARISON.md`](docs/internal/ROOMBAPY_COMPARISON.md) for the
|
|
509
|
+
full comparison (including a size/structure breakdown of both libraries).
|
|
510
|
+
|
|
511
|
+
## Documentation
|
|
512
|
+
|
|
513
|
+
**Start here:** [`docs/API_REFERENCE.md`](docs/API_REFERENCE.md) (every
|
|
514
|
+
method and model) and [`CHANGELOG.md`](CHANGELOG.md) (what's changed,
|
|
515
|
+
release by release).
|
|
516
|
+
|
|
517
|
+
Everything else — the session-by-session reverse-engineering trail
|
|
518
|
+
(`docs/internal/`) and a handful of superseded early drafts
|
|
519
|
+
(`docs/archive/`) — is background material, not needed to use the
|
|
520
|
+
library. See the comment at the top of each folder's files for what's
|
|
521
|
+
there and why.
|
|
522
|
+
- [Setting value sets](docs/internal/SETTING_VALUE_SETS.md) — why four of the six writable settings cannot have a picker
|
|
523
|
+
- [APK 3.0.0 findings](docs/internal/APK_3_0_0_FINDINGS.md) — what iRobot's own app corrected, and what was deliberately left alone
|
|
524
|
+
|
|
525
|
+
## Credits
|
|
526
|
+
|
|
527
|
+
- **[roombapy](https://github.com/pschmitt/roombapy)** (pschmitt and
|
|
528
|
+
contributors) — the Classic-protocol client this project doesn't
|
|
529
|
+
extend (see [above](#why-not-just-extend-roombapy)), but whose
|
|
530
|
+
design this project learned from throughout: `prime_robot.py`
|
|
531
|
+
mirrors its public-class pattern, `prime_factory.py` mirrors its
|
|
532
|
+
factory pattern, and the TLS-verification discussion in
|
|
533
|
+
[`SECURITY.md`](SECURITY.md) directly contrasts with its
|
|
534
|
+
local-network `ssl.CERT_NONE` approach (correct for its use case,
|
|
535
|
+
not for this one).
|
|
536
|
+
- **[Ader](https://github.com/lvigilantecorreo-commits)** —
|
|
537
|
+
maintainer of
|
|
538
|
+
**[roomba-v4](https://github.com/lvigilantecorreo-commits/roomba-v4)**,
|
|
539
|
+
the first public reverse-engineering work on the V4/Prime command
|
|
540
|
+
path, and the project that triggered this library's development in
|
|
541
|
+
the first place. Since then, an ongoing two-way exchange of
|
|
542
|
+
cross-verification findings between the two independent projects —
|
|
543
|
+
including confirming that room/zone-targeting is real, found
|
|
544
|
+
directly in the app's own binary under the internal name `p2maps`,
|
|
545
|
+
now the central concept this entire library is organized around.
|
|
546
|
+
- **chairstacker** — this project's primary field tester. Confirmed
|
|
547
|
+
mission control working live against a real robot (the single
|
|
548
|
+
biggest open question this library had for most of its life), and
|
|
549
|
+
a detailed `--dump-config` capture from a real account surfaced
|
|
550
|
+
three genuine crash bugs and a write-side bug that static analysis
|
|
551
|
+
alone had missed. Most of what this library can say "confirmed
|
|
552
|
+
live" about, it can say because of this testing.
|
|
553
|
+
- **jadestar1864** — a second, independent Prime account (same
|
|
554
|
+
robot model as chairstacker's, different household) — the first
|
|
555
|
+
confirmation that this library's behavior is consistent across
|
|
556
|
+
more than one real account, not just one lucky match.
|
|
557
|
+
|
|
558
|
+
## License
|
|
559
|
+
|
|
560
|
+
MIT — see [`LICENSE`](LICENSE).
|