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.
Files changed (53) hide show
  1. roombapy_prime-0.3.1/LICENSE +21 -0
  2. roombapy_prime-0.3.1/PKG-INFO +560 -0
  3. roombapy_prime-0.3.1/README.md +526 -0
  4. roombapy_prime-0.3.1/pyproject.toml +83 -0
  5. roombapy_prime-0.3.1/roombapy_prime/__init__.py +95 -0
  6. roombapy_prime-0.3.1/roombapy_prime/auth.py +1002 -0
  7. roombapy_prime-0.3.1/roombapy_prime/aws_sigv4.py +126 -0
  8. roombapy_prime-0.3.1/roombapy_prime/diagnostics.py +1097 -0
  9. roombapy_prime-0.3.1/roombapy_prime/ids.py +96 -0
  10. roombapy_prime-0.3.1/roombapy_prime/models/__init__.py +38 -0
  11. roombapy_prime-0.3.1/roombapy_prime/models/enums_common.py +117 -0
  12. roombapy_prime-0.3.1/roombapy_prime/models/favorites.py +146 -0
  13. roombapy_prime-0.3.1/roombapy_prime/models/geometry.py +87 -0
  14. roombapy_prime-0.3.1/roombapy_prime/models/livemap.py +392 -0
  15. roombapy_prime-0.3.1/roombapy_prime/models/map_bundle.py +1156 -0
  16. roombapy_prime-0.3.1/roombapy_prime/models/map_editing.py +1137 -0
  17. roombapy_prime-0.3.1/roombapy_prime/models/mission_control.py +1581 -0
  18. roombapy_prime-0.3.1/roombapy_prime/models/mission_history.py +1456 -0
  19. roombapy_prime-0.3.1/roombapy_prime/models/robot_info.py +3982 -0
  20. roombapy_prime-0.3.1/roombapy_prime/models/schedules_dnd.py +671 -0
  21. roombapy_prime-0.3.1/roombapy_prime/models/time_estimates.py +262 -0
  22. roombapy_prime-0.3.1/roombapy_prime/mqtt_client.py +1608 -0
  23. roombapy_prime-0.3.1/roombapy_prime/prime_factory.py +111 -0
  24. roombapy_prime-0.3.1/roombapy_prime/prime_robot.py +1985 -0
  25. roombapy_prime-0.3.1/roombapy_prime/py.typed +0 -0
  26. roombapy_prime-0.3.1/roombapy_prime/rest_client.py +1812 -0
  27. roombapy_prime-0.3.1/roombapy_prime/tests/conftest.py +28 -0
  28. roombapy_prime-0.3.1/roombapy_prime/tests/test_auth.py +1207 -0
  29. roombapy_prime-0.3.1/roombapy_prime/tests/test_aws_sigv4.py +99 -0
  30. roombapy_prime-0.3.1/roombapy_prime/tests/test_diagnostics.py +793 -0
  31. roombapy_prime-0.3.1/roombapy_prime/tests/test_favorites.py +117 -0
  32. roombapy_prime-0.3.1/roombapy_prime/tests/test_ids.py +112 -0
  33. roombapy_prime-0.3.1/roombapy_prime/tests/test_init.py +80 -0
  34. roombapy_prime-0.3.1/roombapy_prime/tests/test_livemap.py +54 -0
  35. roombapy_prime-0.3.1/roombapy_prime/tests/test_map_bundle.py +89 -0
  36. roombapy_prime-0.3.1/roombapy_prime/tests/test_map_editing.py +292 -0
  37. roombapy_prime-0.3.1/roombapy_prime/tests/test_models.py +7352 -0
  38. roombapy_prime-0.3.1/roombapy_prime/tests/test_mqtt_client.py +1700 -0
  39. roombapy_prime-0.3.1/roombapy_prime/tests/test_prime_factory.py +175 -0
  40. roombapy_prime-0.3.1/roombapy_prime/tests/test_prime_robot.py +2431 -0
  41. roombapy_prime-0.3.1/roombapy_prime/tests/test_rest_client.py +1579 -0
  42. roombapy_prime-0.3.1/roombapy_prime/tests/test_schedules_dnd.py +59 -0
  43. roombapy_prime-0.3.1/roombapy_prime/tests/test_time_estimates.py +241 -0
  44. roombapy_prime-0.3.1/roombapy_prime/tests/test_tools_boundary.py +67 -0
  45. roombapy_prime-0.3.1/roombapy_prime/vendor_errors.py +3057 -0
  46. roombapy_prime-0.3.1/roombapy_prime/vendor_reference.json +7838 -0
  47. roombapy_prime-0.3.1/roombapy_prime/vendor_reference.py +106 -0
  48. roombapy_prime-0.3.1/roombapy_prime.egg-info/PKG-INFO +560 -0
  49. roombapy_prime-0.3.1/roombapy_prime.egg-info/SOURCES.txt +51 -0
  50. roombapy_prime-0.3.1/roombapy_prime.egg-info/dependency_links.txt +1 -0
  51. roombapy_prime-0.3.1/roombapy_prime.egg-info/requires.txt +14 -0
  52. roombapy_prime-0.3.1/roombapy_prime.egg-info/top_level.txt +1 -0
  53. 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
+ [![CI](https://github.com/johnnyh1975/roombapy-prime/actions/workflows/ci.yml/badge.svg)](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).