python-mobius 0.8.4__tar.gz → 0.10.0__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 (138) hide show
  1. python_mobius-0.10.0/CHANGELOG.md +260 -0
  2. python_mobius-0.10.0/PKG-INFO +161 -0
  3. python_mobius-0.10.0/README.md +129 -0
  4. python_mobius-0.10.0/documentation/00-overview.md +92 -0
  5. python_mobius-0.10.0/documentation/01-ble-transport.md +71 -0
  6. python_mobius-0.10.0/documentation/02-framing-and-crc.md +131 -0
  7. python_mobius-0.10.0/documentation/03-attributes-and-opcodes.md +150 -0
  8. python_mobius-0.10.0/documentation/04-device-identity.md +113 -0
  9. python_mobius-0.10.0/documentation/05-scenes.md +122 -0
  10. python_mobius-0.10.0/documentation/06-light-schedule.md +327 -0
  11. python_mobius-0.10.0/documentation/07-pump-schedule.md +102 -0
  12. python_mobius-0.10.0/documentation/08-manufacturer-data.md +63 -0
  13. python_mobius-0.10.0/documentation/09-thread-coap-relay.md +187 -0
  14. python_mobius-0.10.0/documentation/10-known-gaps-and-open-questions.md +62 -0
  15. python_mobius-0.10.0/documentation/11-device-status-attributes.md +190 -0
  16. python_mobius-0.10.0/documentation/12-device-identity-and-address-stability.md +31 -0
  17. python_mobius-0.10.0/documentation/13-attribute-dump.md +64 -0
  18. python_mobius-0.10.0/documentation/14-attribute-reference.md +667 -0
  19. python_mobius-0.10.0/documentation/15-mob-template-format.md +64 -0
  20. python_mobius-0.10.0/documentation/16-python-library.md +172 -0
  21. python_mobius-0.10.0/documentation/17-configuration-checksums.md +84 -0
  22. {python_mobius-0.8.4 → python_mobius-0.10.0}/pyproject.toml +1 -1
  23. {python_mobius-0.8.4 → python_mobius-0.10.0}/src/mobius/__init__.py +29 -20
  24. python_mobius-0.10.0/src/mobius/checksums.py +119 -0
  25. python_mobius-0.10.0/src/mobius/cli.py +1113 -0
  26. python_mobius-0.10.0/src/mobius/coap.py +158 -0
  27. {python_mobius-0.8.4 → python_mobius-0.10.0}/src/mobius/constants.py +269 -277
  28. {python_mobius-0.8.4 → python_mobius-0.10.0}/src/mobius/crc.py +3 -5
  29. python_mobius-0.10.0/src/mobius/device.py +2783 -0
  30. python_mobius-0.10.0/src/mobius/device_status.py +431 -0
  31. python_mobius-0.10.0/src/mobius/discovery.py +190 -0
  32. python_mobius-0.10.0/src/mobius/dump.py +253 -0
  33. python_mobius-0.10.0/src/mobius/frame.py +216 -0
  34. python_mobius-0.10.0/src/mobius/manufacturer.py +70 -0
  35. python_mobius-0.10.0/src/mobius/mesh_address.py +96 -0
  36. python_mobius-0.10.0/src/mobius/mob.py +132 -0
  37. python_mobius-0.10.0/src/mobius/modifiers.py +134 -0
  38. python_mobius-0.10.0/src/mobius/power.py +65 -0
  39. python_mobius-0.10.0/src/mobius/pump_status.py +39 -0
  40. python_mobius-0.10.0/src/mobius/relay.py +225 -0
  41. python_mobius-0.10.0/src/mobius/scenes.py +114 -0
  42. python_mobius-0.10.0/src/mobius/schedule.py +353 -0
  43. python_mobius-0.10.0/src/mobius/timezone.py +63 -0
  44. python_mobius-0.10.0/tests/test_battery_settings.py +143 -0
  45. python_mobius-0.10.0/tests/test_bluetooth_only.py +119 -0
  46. python_mobius-0.10.0/tests/test_checksums.py +98 -0
  47. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_coap.py +2 -6
  48. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_coap_indication_handling.py +4 -10
  49. python_mobius-0.10.0/tests/test_configuration_cache.py +259 -0
  50. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_device_control.py +3 -6
  51. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_device_status.py +1 -2
  52. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_full_poll_batch.py +51 -0
  53. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_light_poll_batch.py +20 -0
  54. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_light_schedule.py +2 -10
  55. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_manufacturer_lookup.py +3 -7
  56. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_metadata_batch.py +18 -0
  57. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_mob.py +2 -4
  58. python_mobius-0.10.0/tests/test_pump_param_ranges.py +81 -0
  59. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_pump_reverse.py +4 -9
  60. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_relay.py +55 -26
  61. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_scenes.py +137 -8
  62. python_mobius-0.10.0/tests/test_shared_helpers.py +85 -0
  63. python_mobius-0.10.0/tests/test_time_zone.py +68 -0
  64. python_mobius-0.10.0/tests/test_write_size.py +64 -0
  65. python_mobius-0.8.4/CHANGELOG.md +0 -890
  66. python_mobius-0.8.4/PKG-INFO +0 -208
  67. python_mobius-0.8.4/README.md +0 -176
  68. python_mobius-0.8.4/documentation/00-overview.md +0 -81
  69. python_mobius-0.8.4/documentation/01-ble-transport.md +0 -67
  70. python_mobius-0.8.4/documentation/02-framing-and-crc.md +0 -82
  71. python_mobius-0.8.4/documentation/03-attributes-and-opcodes.md +0 -97
  72. python_mobius-0.8.4/documentation/04-device-identity.md +0 -81
  73. python_mobius-0.8.4/documentation/05-scenes.md +0 -137
  74. python_mobius-0.8.4/documentation/06-light-schedule.md +0 -454
  75. python_mobius-0.8.4/documentation/07-pump-schedule.md +0 -185
  76. python_mobius-0.8.4/documentation/08-manufacturer-data.md +0 -95
  77. python_mobius-0.8.4/documentation/09-thread-coap-relay.md +0 -369
  78. python_mobius-0.8.4/documentation/10-known-gaps-and-open-questions.md +0 -171
  79. python_mobius-0.8.4/documentation/11-device-status-attributes.md +0 -336
  80. python_mobius-0.8.4/documentation/12-device-identity-and-address-stability.md +0 -83
  81. python_mobius-0.8.4/documentation/13-attribute-dump.md +0 -246
  82. python_mobius-0.8.4/documentation/14-batched-attribute-reads.md +0 -112
  83. python_mobius-0.8.4/documentation/15-mob-template-format.md +0 -107
  84. python_mobius-0.8.4/src/mobius/cli.py +0 -1380
  85. python_mobius-0.8.4/src/mobius/coap.py +0 -250
  86. python_mobius-0.8.4/src/mobius/device.py +0 -4211
  87. python_mobius-0.8.4/src/mobius/device_status.py +0 -751
  88. python_mobius-0.8.4/src/mobius/discovery.py +0 -274
  89. python_mobius-0.8.4/src/mobius/dump.py +0 -377
  90. python_mobius-0.8.4/src/mobius/frame.py +0 -266
  91. python_mobius-0.8.4/src/mobius/manufacturer.py +0 -73
  92. python_mobius-0.8.4/src/mobius/mesh_address.py +0 -136
  93. python_mobius-0.8.4/src/mobius/mob.py +0 -188
  94. python_mobius-0.8.4/src/mobius/modifiers.py +0 -169
  95. python_mobius-0.8.4/src/mobius/power.py +0 -91
  96. python_mobius-0.8.4/src/mobius/pump_status.py +0 -50
  97. python_mobius-0.8.4/src/mobius/relay.py +0 -496
  98. python_mobius-0.8.4/src/mobius/scenes.py +0 -97
  99. python_mobius-0.8.4/src/mobius/schedule.py +0 -418
  100. {python_mobius-0.8.4 → python_mobius-0.10.0}/.forgejo/scripts/extract_changelog_section.py +0 -0
  101. {python_mobius-0.8.4 → python_mobius-0.10.0}/.forgejo/workflows/release.yml +0 -0
  102. {python_mobius-0.8.4 → python_mobius-0.10.0}/.gitignore +0 -0
  103. {python_mobius-0.8.4 → python_mobius-0.10.0}/LICENSE +0 -0
  104. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_attributes_batch.py +0 -0
  105. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_cli_group_targets.py +0 -0
  106. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_cli_mob_file.py +0 -0
  107. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_cli_schedule_file.py +0 -0
  108. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_connection_cleanup.py +0 -0
  109. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_device_summary.py +0 -0
  110. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_device_time.py +0 -0
  111. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_discovery_company_ids.py +0 -0
  112. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_dump.py +0 -0
  113. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_extra_physical_values.py +0 -0
  114. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_fan_thresholds.py +0 -0
  115. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_firmware_versions.py +0 -0
  116. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_frame.py +0 -0
  117. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_frame_sniffer.py +0 -0
  118. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_group_bitmap.py +0 -0
  119. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_hyperdrive_device.py +0 -0
  120. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_insolation_detection.py +0 -0
  121. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_light_intensity_branching.py +0 -0
  122. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_manufacturer.py +0 -0
  123. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_mesh_address.py +0 -0
  124. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_mesh_discovery.py +0 -0
  125. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_mesh_discovery_direct_connect.py +0 -0
  126. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_modifiers.py +0 -0
  127. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_networked_thread_devices.py +0 -0
  128. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_new_diagnostic_attributes.py +0 -0
  129. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_power.py +0 -0
  130. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_pump_schedule.py +0 -0
  131. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_pump_status.py +0 -0
  132. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_pump_supported_modes.py +0 -0
  133. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_pump_telemetry.py +0 -0
  134. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_schedule_file_format.py +0 -0
  135. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_schedule_intensity_scalar.py +0 -0
  136. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_serial_decoding.py +0 -0
  137. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_serial_identity.py +0 -0
  138. {python_mobius-0.8.4 → python_mobius-0.10.0}/tests/test_supported_attributes.py +0 -0
@@ -0,0 +1,260 @@
1
+ # Changelog
2
+
3
+ ## 0.10.0
4
+
5
+ - Added `set_time_zone()`; the batched polls now include the device's time
6
+ zone strings.
7
+ - Relayed requests now accept a late response to the first attempt, as
8
+ the app does.
9
+ - Fixed an unreachable light returning an empty poll as a success.
10
+ - Added configuration checksums (`get_configuration_checksums()`, CLI
11
+ `--check-checksums`).
12
+ - `get_full_poll_batch(cache=...)` reads scenes and schedules only when
13
+ their checksum changed.
14
+ - `start_scene()` now runs a scene for its configured length by default
15
+ (a duration of 0 makes the device reuse its last scene length).
16
+ - Fixed a scene briefly reporting about 18 hours remaining as it ends.
17
+ - Added Bluetooth-only device support (Blades, MXM): `is_bluetooth_only()`
18
+ and `find_bluetooth_only_tank_members()`. The CLI connects to them
19
+ directly and includes them in schedule group writes.
20
+ - Writes use the negotiated MTU instead of 20-byte fragments, as the app
21
+ does (fewer writes per request).
22
+ - The pump's operation state is now part of the batched poll.
23
+ - `ConfigurationCache(static_refresh_interval=...)` reads rarely-changing
24
+ attributes (identity, firmware, settings) only at that interval.
25
+
26
+ ## 0.9.0
27
+
28
+ - **Breaking:** `RelayedMobiusDevice` writes now raise an error when the
29
+ device rejects them, like direct writes already did.
30
+ - Added helpers shared with ha-mobius: `enum_or_none()`,
31
+ `primitive_type_from_name()`, `support_tier()`, `parse_advertisement()`,
32
+ `schedule_capacity()`/`min_schedule_capacity()`, `format_mesh_address()`,
33
+ `pump_params_to_dict()`, `Scene.is_empty` and `PUMP_PRIMITIVES`.
34
+ - Added pump battery settings: `set_battery_backup_speed()` (limited to the
35
+ pump's battery backup max speed) and `set_boosted_battery()` (only while
36
+ running on battery), with the `--set-battery-backup-speed` and
37
+ `--set-boosted-battery-*` options.
38
+ - Added `get_operation_mode()` and the `OperationMode` enum; pump telemetry
39
+ now includes `operation_mode` (`"BatteryBackup"` while running on
40
+ battery).
41
+ - `get_full_poll_batch()` now includes the battery settings
42
+ (`battery_backup`, `boosted_battery`) and the operation mode.
43
+ - The boosted battery power is permille (0-100 %) and its on/off times are
44
+ seconds.
45
+ - `mobius-scan`: the debug mesh listing now shows peer age as unsigned, like
46
+ the library.
47
+ - **Breaking:** fixed the running scene's remaining time: it's now read from
48
+ `SceneTimer`, so it counts down, and `ActiveScene.timeout_seconds` holds
49
+ the scene's total time. `get_current_scene()` returns None when no scene
50
+ is running.
51
+ - Starting a scene, returning to the schedule and restarting all devices now
52
+ use the tank's group, like the app (`get_tank_group()`, `tank_group()`).
53
+ **Breaking:** `resume_schedule()` now ends the scene on every device of
54
+ the tank with one write (`broadcast=False` for one device only).
55
+ - **Breaking:** `set_pump_schedule()` now rejects parameters outside the
56
+ ranges the app allows (for example a Short Pulse time of 0), before
57
+ writing. Added `pump_param_range()` and `pump_param_errors()`.
58
+ - Internal cleanup: removed duplicated code and simplified comments.
59
+
60
+ ## 0.8.4
61
+
62
+ - Added `LightPollResult.lunar_enabled` and `lunar_phase_day`, valid at any
63
+ time of day (the matching `diagnostics` keys are only set at night).
64
+
65
+ ## 0.8.3
66
+
67
+ - Added `set_lunar_enabled(enabled)`: the value the app's Lunar chip
68
+ writes.
69
+
70
+ ## 0.8.2
71
+
72
+ - Added `LightPollResult.schedule_intensity`: the schedule intensity
73
+ setting (unlike `diagnostics["scalar"]`, which is the lunar reduction at
74
+ night).
75
+
76
+ ## 0.8.1
77
+
78
+ - Added `get_pump_reverse(primitive_type, params)`: whether a pump
79
+ point's speed means reverse rotation (AlpacaV1 only).
80
+ - Added `reboot_all()`: restarts every device of the tank, like the app's
81
+ "Restart all Devices".
82
+
83
+ ## 0.8.0
84
+
85
+ - Added `supported_pump_modes(primitive_type, closed_loop=None)`: the pump
86
+ modes each pump supports (see `documentation/07-pump-schedule.md`,
87
+ "Modes per pump").
88
+ - Added schedule writing: `set_light_schedule()`, `set_pump_schedule()`,
89
+ `GroupBitmap` (`group_mask`), and the `--dump-schedule-file` /
90
+ `--load-schedule-file` options.
91
+ - Added `.mob` template files: export and import for light and pump
92
+ schedules, and the `--dump-mob-file` / `--load-mob-file` options.
93
+ - Loading a schedule into a light now also writes every light in its
94
+ schedule group, after checking the group's smallest capacity.
95
+ - `get_pump_telemetry()` now includes every other sensor value the pump
96
+ reports (`extra_physical_values`).
97
+ - Added `get_extra_physical_values()`, also shown for lights in
98
+ `get_device_summary()`.
99
+ - Added `get_fan_thresholds()`.
100
+
101
+ ## 0.7.2
102
+
103
+ - `get_full_poll_batch()` now includes the scenes.
104
+ - `start_scene()` / `start_feed_mode()` now apply to the whole tank by
105
+ default; `broadcast=False` writes one device only.
106
+
107
+ ## 0.7.1
108
+
109
+ - Added scenes: `get_configured_scenes()`, `get_current_scene()`,
110
+ `start_scene()`.
111
+ - Fixed `get_current_scene()` failing on user-created scenes.
112
+ - `start_scene()`'s duration is in seconds (it was documented as minutes).
113
+
114
+ ## 0.7.0
115
+
116
+ - Added `get_full_poll_batch()`: everything a poll needs in one request,
117
+ 2-2.6× faster.
118
+ - Added `get_light_poll_batch()`: a light's schedule and intensity inputs
119
+ in one request.
120
+ - `get_current_pump_block()` accepts an already-read schedule (`points`).
121
+
122
+ ## 0.6.0
123
+
124
+ - Added `get_metadata_batch()`: settings, calibration, firmware, hardware,
125
+ channels, error state and clock in one request (6-14× faster over the
126
+ relay), with a fallback to individual reads (`used_batch`,
127
+ `force_individual_reads`).
128
+ - Added `get_attributes_batch()`: several attributes in one request.
129
+ - Added attribute dumps: `get_supported_attributes()`,
130
+ `dump_attributes()`, `enrich_attribute_dump()` and the
131
+ `--dump-attributes` / `--parse-dump` options.
132
+ - Added `set_advanced_features()` and the `--set-local-control-enabled`,
133
+ `--set-auto-dim-timeout`, `--set-max-fan-speed` and
134
+ `--set-fan-shutdown-enabled` options.
135
+ - The lunar calculation now uses the same date as the app, so the values
136
+ match what the app shows.
137
+ - Fixed `RelayedMobiusDevice` not relaying `get_supported_attributes()`
138
+ and `get_attributes_batch()`.
139
+ - Fixed `--dump-attributes` and `--set-time-to-now` ignoring
140
+ `--relay-target`.
141
+
142
+ ## 0.5.0
143
+
144
+ - `get_pump_telemetry()` now returns `gph_reliable`, `minimum_gph` and
145
+ `maximum_gph`; flow is only reliable when the pump reports a flow range.
146
+ - Added `motor_power_watts` to `get_pump_telemetry()` (Vectra, NYOS
147
+ Quantum).
148
+ - Added support for AquaIllumination devices (company ID `0x0001`).
149
+ **Breaking:** `MOBIUS_COMPANY_ID` is now `MOBIUS_COMPANY_ID_ECOTECH`.
150
+ - Added `get_lunar_phase_info()` (the device's own lunar state) and show
151
+ it in `--dump-schedule`. Read errors are now raised instead of
152
+ returning `None`.
153
+ - Added `get_advanced_features()`, `get_vectra_info()` and
154
+ `get_coffee_info()`.
155
+ - Added `set_time_to_now()` (sets the clock of the whole tank) and
156
+ `reboot()`.
157
+ - Fixed firmware labels for AquaIllumination devices.
158
+ - A relayed device that timed out now fails its other reads in the same
159
+ poll immediately.
160
+
161
+ ## 0.4.4
162
+
163
+ - The device model list is now complete (EcoTech Marine,
164
+ AquaIllumination, Neptune Systems, NYOS). `Model.VersaVX1` is now listed
165
+ under EcoTech Marine.
166
+ - Several models are now named after their product (e.g. `Model.Axis40`,
167
+ `Model.Blade`, `Model.Quantum160`).
168
+ - Fixed the lunar-dimmed light intensity disagreeing with the app for
169
+ hours at a time.
170
+
171
+ ## 0.4.3
172
+
173
+ - Relayed reads are retried once before failing, like the app does.
174
+
175
+ ## 0.4.2
176
+
177
+ - Fixed `MeshPeer.age` being read as signed. It is the time since the peer
178
+ was last heard on the mesh, so it can go down.
179
+ - Fixed `mobius-scan --dump-schedule` failing for pumps.
180
+
181
+ ## 0.4.1
182
+
183
+ - Fixed `discover_tank()` sometimes listing the connected device twice.
184
+
185
+ ## 0.4.0
186
+
187
+ - Added mesh discovery through `NetworkedThreadDevices`, which works
188
+ without an MXM hub: `discover_networked_thread_devices()`, `MeshPeer.age`
189
+ and `mesh_local_prefix_from_mlprefix()`.
190
+ - Added `discover_mesh_peers_auto()`, the recommended way to discover the
191
+ tank's devices.
192
+ - Added `Tank` and `discover_tank()`: every device of a tank with its mesh
193
+ address and age.
194
+ - `mobius-scan` now lists devices by tank and connects to one device to
195
+ show the tank's mesh addresses; `--scan-only` skips connecting,
196
+ `--full-summaries` connects to every device and `--dump-tanks` shows
197
+ every tank.
198
+
199
+ ## 0.3.1
200
+
201
+ - Fixed the Brightness channel not dimming the other channels, which
202
+ made light intensities too high.
203
+
204
+ ## 0.3.0
205
+
206
+ - Fixed lunar dimming being applied to the wrong part of the night.
207
+ - `get_hardware_info()` now returns labels for colour, product, radio and
208
+ motor type, and is included in `get_device_summary()`.
209
+
210
+ ## 0.2.1
211
+
212
+ - Fixed lists that the device returns in several parts being cut short
213
+ (for example some firmware versions were missing).
214
+
215
+ ## 0.2.0
216
+
217
+ - Added relaying through the Thread mesh: `RelayedMobiusDevice` reaches
218
+ every device of a tank through one BLE connection. Added `mobius.coap`
219
+ and `mobius.mesh_address`.
220
+ - Added mesh discovery: `get_own_mesh_address()`,
221
+ `discover_mesh_peers()` (MXM hub) and
222
+ `discover_mesh_peers_via_direct_connect()`.
223
+ - Added the `mobius-scan` options `--dump-mesh-peers`, `--relay-target`,
224
+ `--debug-relay`, `--build-peer-map` and `--dump-schedule`.
225
+ - Added `get_device_time_info()` (clock drift).
226
+ - `get_current_light_intensities()` now reports how its result was
227
+ computed in `.diagnostics`.
228
+
229
+ ## 0.1.4
230
+
231
+ - Added `MobiusDevice.is_connected`.
232
+
233
+ ## 0.1.3
234
+
235
+ - Devices are now identified by serial number instead of BLE address,
236
+ which can change: `find_device_by_serial()`, `dedupe_by_serial()`,
237
+ `MobiusDevice(serial=...)` and `mobius-scan --by-serial`.
238
+ - Added `get_firmware_versions()` and `get_hardware_info()`.
239
+ - Added `get_group_info()`, `get_calibration_info()` and
240
+ `get_maintenance_info()`.
241
+ - Added `get_pump_flow_range()`, `get_pump_override_mode()`,
242
+ `get_battery_backup_info()` and `get_boosted_battery_info()`, included
243
+ in `get_device_summary()`.
244
+ - Light intensities now include the schedule intensity, lunar phases,
245
+ acclimation and hyperdrive (`get_current_light_percentages()`), and flag
246
+ Insolation (not supported).
247
+
248
+ ## 0.1.2
249
+
250
+ - Added `manufacturer_for_model()` / `MODEL_MANUFACTURER`.
251
+ - Serial numbers are returned as text instead of hex.
252
+ - `get_pump_telemetry()` now includes `speed_percent`.
253
+ - Fixed a connection left open when subscribing to notifications failed.
254
+
255
+ ## 0.1.0
256
+
257
+ - First release: framing, CRC and attribute get/set; scenes; pump
258
+ telemetry and schedules; light schedules and current intensities;
259
+ device identity and `get_device_summary()`; discovery and tank grouping
260
+ from advertisements; the `mobius-scan` command.
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.5
2
+ Name: python-mobius
3
+ Version: 0.10.0
4
+ Summary: Reverse-engineered Python client for the Mobius BLE protocol (EcoTech Marine VorTech/Radion, AquaIllumination, Neptune Systems, NYOS)
5
+ Project-URL: Homepage, https://code.r3pek.org/r3pek/python-mobius
6
+ Project-URL: Documentation, https://code.r3pek.org/r3pek/python-mobius/src/branch/main/documentation
7
+ Project-URL: Issues, https://code.r3pek.org/r3pek/python-mobius/issues
8
+ Author: r3pek
9
+ License: GPL-2.0-only
10
+ License-File: LICENSE
11
+ Keywords: aquarium,ble,bleak,bluetooth,ecotech,fsci,mobius,radion,reef,vortech
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2)
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Home Automation
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: System :: Hardware
23
+ Requires-Python: >=3.9
24
+ Requires-Dist: bleak>=0.21
25
+ Provides-Extra: dev
26
+ Requires-Dist: bleak-retry-connector>=3.0; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0; extra == 'dev'
29
+ Provides-Extra: retry
30
+ Requires-Dist: bleak-retry-connector>=3.0; extra == 'retry'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # python-mobius
34
+
35
+ A Python client for the Bluetooth protocol of "Mobius" aquarium devices:
36
+ EcoTech Marine (VorTech and Vectra pumps, Radion lights),
37
+ AquaIllumination (Prime, Hydra, Orbit, Axis), Neptune Systems and NYOS.
38
+ Built on [`bleak`](https://github.com/hbldh/bleak).
39
+
40
+ **Not affiliated with or endorsed by any of these companies.** This is an
41
+ independent implementation of the protocol for interoperability with
42
+ hardware you own, based on reverse engineering of the Mobius app and
43
+ public community research. The protocol is documented in
44
+ [`documentation/`](./documentation/00-overview.md), in enough detail to
45
+ implement it in another language.
46
+
47
+ ## Features
48
+
49
+ - **Discovery without connecting**: model, serial number and tank (pan_id)
50
+ from BLE advertisements.
51
+ - **Lights**: channels, schedules, and the current intensity computed the
52
+ way the app does it (schedule intensity, lunar phases, acclimation,
53
+ hyperdrive).
54
+ - **Pumps**: speed, flow, motor power and other sensor values; schedules
55
+ and the active mode; battery backup settings and whether the pump runs
56
+ on battery.
57
+ - **Schedules**: read and write, as editable JSON or as the app's `.mob`
58
+ template files; light schedule groups are handled.
59
+ - **Scenes**: list, start (whole tank) and cancel.
60
+ - **Settings and commands**: advanced features (Local Control, LED auto
61
+ dim, fan speed, fan shutdown), lunar phases, schedule intensity, time
62
+ sync, reboot, restart all.
63
+ - **One connection per tank**: every other device of the tank is reached
64
+ through it over the Thread mesh (`RelayedMobiusDevice`).
65
+ - **Batched polling**: everything a poll needs in one request.
66
+ - **Diagnostics**: attribute dumps, firmware and hardware information,
67
+ Thread and clock diagnostics.
68
+ - **`mobius-scan`** command line tool.
69
+
70
+ ## Supported devices
71
+
72
+ | PrimitiveType | Devices | Support |
73
+ |---|---|---|
74
+ | `VisualV1` | Radion, Prime, Hydra | Tested |
75
+ | `VorTechV1`, `TurtleV1` | VorTech, Axis | Tested |
76
+ | `PumpV1`, `VectraV1`, `AlpacaV1` | Nero, Vectra, Orbit | Same format as the tested pumps; not tested |
77
+ | `CoffeeV1` | NYOS Quantum | Experimental: decoded with the pump format, not tested |
78
+ | `DoseV1`, `HotSauceV1` | Dosers, sensors | Identity only |
79
+
80
+ See [known gaps](./documentation/10-known-gaps-and-open-questions.md) for
81
+ what isn't covered or tested.
82
+
83
+ ## Install
84
+
85
+ ```bash
86
+ pip install python-mobius
87
+ # recommended: more reliable connections
88
+ pip install "python-mobius[retry]"
89
+ ```
90
+
91
+ Python 3.9 or newer.
92
+
93
+ ## Quick start
94
+
95
+ ```python
96
+ import asyncio
97
+ from mobius import scan_for_mobius_devices_with_info, group_by_pan_id, MobiusDevice
98
+
99
+ async def main():
100
+ found = await scan_for_mobius_devices_with_info()
101
+ for pan_id, members in group_by_pan_id(found).items():
102
+ print(f"tank {pan_id:#06x}:")
103
+ for device, info in members:
104
+ print(f" {info.serial} {info.model.name if info.model else info.model_raw}")
105
+
106
+ device, info = found[0]
107
+ async with MobiusDevice(device, serial=info.serial) as d:
108
+ print(await d.get_device_summary())
109
+
110
+ asyncio.run(main())
111
+ ```
112
+
113
+ Reaching the rest of the tank through one connection:
114
+
115
+ ```python
116
+ from mobius import RelayedMobiusDevice
117
+
118
+ async with MobiusDevice(serial="00000000000002") as gateway:
119
+ for peer in await gateway.discover_mesh_peers_auto():
120
+ print(await RelayedMobiusDevice(gateway, peer).get_device_info())
121
+ ```
122
+
123
+ Command line:
124
+
125
+ ```bash
126
+ mobius-scan # list devices by tank
127
+ mobius-scan --by-serial SERIAL --dump-schedule
128
+ mobius-scan --by-serial SERIAL --relay-target OTHER_SERIAL
129
+ mobius-scan --help
130
+ ```
131
+
132
+ The full API and every CLI option are described in
133
+ [documentation/16-python-library.md](./documentation/16-python-library.md).
134
+
135
+ ## Development
136
+
137
+ ```bash
138
+ git clone https://code.r3pek.org/r3pek/python-mobius
139
+ cd python-mobius
140
+ pip install -e ".[dev]"
141
+ pytest
142
+ ```
143
+
144
+ Tests use captured packets and advertisements from real devices where
145
+ possible.
146
+
147
+ ## Related
148
+
149
+ [ha-mobius](https://code.r3pek.org/r3pek/ha-mobius) is a Home Assistant
150
+ integration built on this library.
151
+
152
+ ## License
153
+
154
+ GPLv2, see [`LICENSE`](./LICENSE).
155
+
156
+ ## Acknowledgments
157
+
158
+ The Reef2Reef thread "Controlling Mobius enabled VorTech pump using 0-10V
159
+ and BLE" and the `danmrossi/MobiusControl` project, whose work this
160
+ builds on. The reverse engineering and implementation were done with
161
+ substantial help from Claude (Anthropic).
@@ -0,0 +1,129 @@
1
+ # python-mobius
2
+
3
+ A Python client for the Bluetooth protocol of "Mobius" aquarium devices:
4
+ EcoTech Marine (VorTech and Vectra pumps, Radion lights),
5
+ AquaIllumination (Prime, Hydra, Orbit, Axis), Neptune Systems and NYOS.
6
+ Built on [`bleak`](https://github.com/hbldh/bleak).
7
+
8
+ **Not affiliated with or endorsed by any of these companies.** This is an
9
+ independent implementation of the protocol for interoperability with
10
+ hardware you own, based on reverse engineering of the Mobius app and
11
+ public community research. The protocol is documented in
12
+ [`documentation/`](./documentation/00-overview.md), in enough detail to
13
+ implement it in another language.
14
+
15
+ ## Features
16
+
17
+ - **Discovery without connecting**: model, serial number and tank (pan_id)
18
+ from BLE advertisements.
19
+ - **Lights**: channels, schedules, and the current intensity computed the
20
+ way the app does it (schedule intensity, lunar phases, acclimation,
21
+ hyperdrive).
22
+ - **Pumps**: speed, flow, motor power and other sensor values; schedules
23
+ and the active mode; battery backup settings and whether the pump runs
24
+ on battery.
25
+ - **Schedules**: read and write, as editable JSON or as the app's `.mob`
26
+ template files; light schedule groups are handled.
27
+ - **Scenes**: list, start (whole tank) and cancel.
28
+ - **Settings and commands**: advanced features (Local Control, LED auto
29
+ dim, fan speed, fan shutdown), lunar phases, schedule intensity, time
30
+ sync, reboot, restart all.
31
+ - **One connection per tank**: every other device of the tank is reached
32
+ through it over the Thread mesh (`RelayedMobiusDevice`).
33
+ - **Batched polling**: everything a poll needs in one request.
34
+ - **Diagnostics**: attribute dumps, firmware and hardware information,
35
+ Thread and clock diagnostics.
36
+ - **`mobius-scan`** command line tool.
37
+
38
+ ## Supported devices
39
+
40
+ | PrimitiveType | Devices | Support |
41
+ |---|---|---|
42
+ | `VisualV1` | Radion, Prime, Hydra | Tested |
43
+ | `VorTechV1`, `TurtleV1` | VorTech, Axis | Tested |
44
+ | `PumpV1`, `VectraV1`, `AlpacaV1` | Nero, Vectra, Orbit | Same format as the tested pumps; not tested |
45
+ | `CoffeeV1` | NYOS Quantum | Experimental: decoded with the pump format, not tested |
46
+ | `DoseV1`, `HotSauceV1` | Dosers, sensors | Identity only |
47
+
48
+ See [known gaps](./documentation/10-known-gaps-and-open-questions.md) for
49
+ what isn't covered or tested.
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pip install python-mobius
55
+ # recommended: more reliable connections
56
+ pip install "python-mobius[retry]"
57
+ ```
58
+
59
+ Python 3.9 or newer.
60
+
61
+ ## Quick start
62
+
63
+ ```python
64
+ import asyncio
65
+ from mobius import scan_for_mobius_devices_with_info, group_by_pan_id, MobiusDevice
66
+
67
+ async def main():
68
+ found = await scan_for_mobius_devices_with_info()
69
+ for pan_id, members in group_by_pan_id(found).items():
70
+ print(f"tank {pan_id:#06x}:")
71
+ for device, info in members:
72
+ print(f" {info.serial} {info.model.name if info.model else info.model_raw}")
73
+
74
+ device, info = found[0]
75
+ async with MobiusDevice(device, serial=info.serial) as d:
76
+ print(await d.get_device_summary())
77
+
78
+ asyncio.run(main())
79
+ ```
80
+
81
+ Reaching the rest of the tank through one connection:
82
+
83
+ ```python
84
+ from mobius import RelayedMobiusDevice
85
+
86
+ async with MobiusDevice(serial="00000000000002") as gateway:
87
+ for peer in await gateway.discover_mesh_peers_auto():
88
+ print(await RelayedMobiusDevice(gateway, peer).get_device_info())
89
+ ```
90
+
91
+ Command line:
92
+
93
+ ```bash
94
+ mobius-scan # list devices by tank
95
+ mobius-scan --by-serial SERIAL --dump-schedule
96
+ mobius-scan --by-serial SERIAL --relay-target OTHER_SERIAL
97
+ mobius-scan --help
98
+ ```
99
+
100
+ The full API and every CLI option are described in
101
+ [documentation/16-python-library.md](./documentation/16-python-library.md).
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ git clone https://code.r3pek.org/r3pek/python-mobius
107
+ cd python-mobius
108
+ pip install -e ".[dev]"
109
+ pytest
110
+ ```
111
+
112
+ Tests use captured packets and advertisements from real devices where
113
+ possible.
114
+
115
+ ## Related
116
+
117
+ [ha-mobius](https://code.r3pek.org/r3pek/ha-mobius) is a Home Assistant
118
+ integration built on this library.
119
+
120
+ ## License
121
+
122
+ GPLv2, see [`LICENSE`](./LICENSE).
123
+
124
+ ## Acknowledgments
125
+
126
+ The Reef2Reef thread "Controlling Mobius enabled VorTech pump using 0-10V
127
+ and BLE" and the `danmrossi/MobiusControl` project, whose work this
128
+ builds on. The reverse engineering and implementation were done with
129
+ substantial help from Claude (Anthropic).
@@ -0,0 +1,92 @@
1
+ # Mobius BLE protocol
2
+
3
+ This directory describes the Bluetooth Low Energy protocol spoken by
4
+ "Mobius" aquarium devices: EcoTech Marine (VorTech and Vectra pumps,
5
+ Radion lights, MXM hub), AquaIllumination (Prime and Hydra lights, Orbit
6
+ and Axis pumps), Neptune Systems and NYOS (Quantum). It is written so the
7
+ protocol can be implemented in any language; Python-specific notes are
8
+ kept in [16-python-library.md](./16-python-library.md).
9
+
10
+ The protocol was worked out through reverse engineering of the official
11
+ Mobius app, public community research (the Reef2Reef thread
12
+ ["Controlling Mobius enabled VorTech pump using 0-10V and BLE"](https://www.reef2reef.com/threads/controlling-mobius-enabled-vortech-pump-using-0-10v-and-ble.814921/)
13
+ and the `danmrossi/MobiusControl` project) and testing on real devices.
14
+
15
+ ## Where to start
16
+
17
+ | If you want to... | Read |
18
+ |---|---|
19
+ | Connect and exchange a first message | [01](./01-ble-transport.md), [02](./02-framing-and-crc.md), [03](./03-attributes-and-opcodes.md) |
20
+ | Find devices and group them by tank without connecting | [08](./08-manufacturer-data.md), [12](./12-device-identity-and-address-stability.md) |
21
+ | Identify what kind of device you are talking to | [04](./04-device-identity.md) |
22
+ | Look up an attribute ID, its encoding or an enum value | [14](./14-attribute-reference.md) |
23
+ | Show what a light is doing right now | [06](./06-light-schedule.md) |
24
+ | Show what a pump is doing right now | [07](./07-pump-schedule.md), [11](./11-device-status-attributes.md) |
25
+ | Read or write schedules | [06](./06-light-schedule.md), [07](./07-pump-schedule.md), [15](./15-mob-template-format.md) |
26
+ | Start or stop scenes (feed mode, ...) | [05](./05-scenes.md) |
27
+ | Reach many devices through one BLE connection | [09](./09-thread-coap-relay.md) |
28
+ | Read settings, diagnostics or send commands (reboot, set time) | [11](./11-device-status-attributes.md) |
29
+ | Dump every attribute of a device | [13](./13-attribute-dump.md) |
30
+ | Avoid re-reading scenes, schedules or the attribute table | [17](./17-configuration-checksums.md) |
31
+ | Know what is not covered or not tested | [10](./10-known-gaps-and-open-questions.md) |
32
+
33
+ ## Document map
34
+
35
+ | File | Covers |
36
+ |---|---|
37
+ | [01-ble-transport.md](./01-ble-transport.md) | GATT service and characteristics, fragmentation, connection notes |
38
+ | [02-framing-and-crc.md](./02-framing-and-crc.md) | Frame layout, opGroups, the reserved field, CRC16, message IDs, status codes |
39
+ | [03-attributes-and-opcodes.md](./03-attributes-and-opcodes.md) | Get/Set attribute, multi-element values, batched reads, listing supported attributes, group writes |
40
+ | [04-device-identity.md](./04-device-identity.md) | `PrimitiveType`, `Model`, support tiers, `ErrorState`, firmware and hardware info |
41
+ | [05-scenes.md](./05-scenes.md) | Configured scenes, starting and cancelling a scene |
42
+ | [06-light-schedule.md](./06-light-schedule.md) | Light schedule format, computing the current intensity, lunar and acclimation modifiers, hyperdrive, writing, schedule groups |
43
+ | [07-pump-schedule.md](./07-pump-schedule.md) | Pump schedule format, modes and parameters, supported modes per pump, writing |
44
+ | [08-manufacturer-data.md](./08-manufacturer-data.md) | Advertisement contents: model, serial and pan_id without connecting |
45
+ | [09-thread-coap-relay.md](./09-thread-coap-relay.md) | Thread mesh addressing, peer discovery, relaying requests through a connected device |
46
+ | [10-known-gaps-and-open-questions.md](./10-known-gaps-and-open-questions.md) | Unimplemented features, untested areas, open questions |
47
+ | [11-device-status-attributes.md](./11-device-status-attributes.md) | Pump telemetry, settings, calibration, maintenance, clock, diagnostics, commands |
48
+ | [12-device-identity-and-address-stability.md](./12-device-identity-and-address-stability.md) | Why devices are identified by serial number, not BLE address |
49
+ | [13-attribute-dump.md](./13-attribute-dump.md) | Reading every supported attribute; dump file formats |
50
+ | [14-attribute-reference.md](./14-attribute-reference.md) | Every known attribute ID and enum value |
51
+ | [15-mob-template-format.md](./15-mob-template-format.md) | The app's `.mob` schedule template file |
52
+ | [16-python-library.md](./16-python-library.md) | How this Python library maps onto the protocol; CLI |
53
+ | [17-configuration-checksums.md](./17-configuration-checksums.md) | Checksums of the attribute table, scenes and schedules |
54
+
55
+ ## Conventions
56
+
57
+ - All multi-byte integers are **little-endian** unless stated otherwise.
58
+ - `u8`/`u16`/`u32`/`u64` are unsigned, `i8`/`i16`/`i32`/`i64` signed.
59
+ - **Permille**: many percentages are sent as tenths of a percent
60
+ (0-1000 = 0.0-100.0 %).
61
+ - Times of day in schedules are **minutes since midnight** (0-1439).
62
+ - Absolute times are **Unix seconds** unless stated otherwise.
63
+ - Strings are UTF-8 (or ASCII), padded with `0x00` to their field size.
64
+ - "Attribute 500" means the attribute with ID 500; names are those used in
65
+ [14-attribute-reference.md](./14-attribute-reference.md).
66
+
67
+ ## Glossary
68
+
69
+ | Term | Meaning |
70
+ |---|---|
71
+ | FSCI / C2CI | Framing layer (FSCI) and the attribute get/set command set on top of it (C2CI) |
72
+ | Attribute | A numbered value on the device (read with Get, written with Set), optionally with several elements addressed by index |
73
+ | Element / index | One value of a multi-value attribute (a schedule point, a firmware component, ...) |
74
+ | Primitive | The device-type-specific payload of a schedule point or scene (channel intensities, pump mode and parameters) |
75
+ | Tank | The set of devices sharing one Thread mesh (same pan_id and mesh prefix) |
76
+ | Gateway | The device a client is connected to over BLE, used to reach the other devices of the tank over Thread |
77
+ | Relayed device | A device reached through the gateway instead of its own BLE connection |
78
+
79
+ ## Verification status
80
+
81
+ Statements about the protocol come from the app's behaviour and, where
82
+ noted, from tests on real hardware (VorTech MP40 pumps, Radion XR15 G6
83
+ lights, an AquaIllumination Axis pump). Parts that were never exercised on
84
+ real hardware are listed in
85
+ [10-known-gaps-and-open-questions.md](./10-known-gaps-and-open-questions.md).
86
+
87
+ ## Legal and scope
88
+
89
+ This is independent reverse engineering for interoperability with hardware
90
+ you own. No vendor code is reproduced; only the protocol facts needed to
91
+ interoperate (wire formats, constants, byte layouts). Not affiliated with
92
+ or endorsed by EcoTech Marine, AquaIllumination, Neptune Systems or NYOS.