python-mobius 0.8.4__tar.gz → 0.9.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 (130) hide show
  1. python_mobius-0.9.0/CHANGELOG.md +237 -0
  2. python_mobius-0.9.0/PKG-INFO +161 -0
  3. python_mobius-0.9.0/README.md +129 -0
  4. python_mobius-0.9.0/documentation/00-overview.md +90 -0
  5. python_mobius-0.9.0/documentation/01-ble-transport.md +67 -0
  6. python_mobius-0.9.0/documentation/02-framing-and-crc.md +131 -0
  7. python_mobius-0.9.0/documentation/03-attributes-and-opcodes.md +149 -0
  8. python_mobius-0.9.0/documentation/04-device-identity.md +113 -0
  9. python_mobius-0.9.0/documentation/05-scenes.md +104 -0
  10. python_mobius-0.9.0/documentation/06-light-schedule.md +327 -0
  11. python_mobius-0.9.0/documentation/07-pump-schedule.md +102 -0
  12. python_mobius-0.9.0/documentation/08-manufacturer-data.md +63 -0
  13. python_mobius-0.9.0/documentation/09-thread-coap-relay.md +160 -0
  14. python_mobius-0.9.0/documentation/10-known-gaps-and-open-questions.md +62 -0
  15. python_mobius-0.9.0/documentation/11-device-status-attributes.md +178 -0
  16. python_mobius-0.9.0/documentation/12-device-identity-and-address-stability.md +31 -0
  17. python_mobius-0.9.0/documentation/13-attribute-dump.md +64 -0
  18. python_mobius-0.9.0/documentation/14-attribute-reference.md +667 -0
  19. python_mobius-0.9.0/documentation/15-mob-template-format.md +64 -0
  20. python_mobius-0.9.0/documentation/16-python-library.md +151 -0
  21. {python_mobius-0.8.4 → python_mobius-0.9.0}/pyproject.toml +1 -1
  22. {python_mobius-0.8.4 → python_mobius-0.9.0}/src/mobius/__init__.py +18 -15
  23. python_mobius-0.9.0/src/mobius/cli.py +984 -0
  24. python_mobius-0.9.0/src/mobius/coap.py +158 -0
  25. {python_mobius-0.8.4 → python_mobius-0.9.0}/src/mobius/constants.py +250 -277
  26. {python_mobius-0.8.4 → python_mobius-0.9.0}/src/mobius/crc.py +3 -5
  27. python_mobius-0.9.0/src/mobius/device.py +2511 -0
  28. python_mobius-0.9.0/src/mobius/device_status.py +427 -0
  29. python_mobius-0.9.0/src/mobius/discovery.py +172 -0
  30. python_mobius-0.9.0/src/mobius/dump.py +253 -0
  31. python_mobius-0.9.0/src/mobius/frame.py +216 -0
  32. python_mobius-0.9.0/src/mobius/manufacturer.py +65 -0
  33. python_mobius-0.9.0/src/mobius/mesh_address.py +96 -0
  34. python_mobius-0.9.0/src/mobius/mob.py +132 -0
  35. python_mobius-0.9.0/src/mobius/modifiers.py +134 -0
  36. python_mobius-0.9.0/src/mobius/power.py +65 -0
  37. python_mobius-0.9.0/src/mobius/pump_status.py +39 -0
  38. python_mobius-0.9.0/src/mobius/relay.py +213 -0
  39. python_mobius-0.9.0/src/mobius/scenes.py +110 -0
  40. python_mobius-0.9.0/src/mobius/schedule.py +353 -0
  41. python_mobius-0.9.0/tests/test_battery_settings.py +143 -0
  42. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_coap.py +2 -6
  43. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_coap_indication_handling.py +4 -10
  44. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_device_control.py +3 -6
  45. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_device_status.py +1 -2
  46. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_light_schedule.py +2 -10
  47. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_manufacturer_lookup.py +3 -7
  48. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_mob.py +2 -4
  49. python_mobius-0.9.0/tests/test_pump_param_ranges.py +81 -0
  50. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_pump_reverse.py +4 -9
  51. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_relay.py +17 -7
  52. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_scenes.py +66 -8
  53. python_mobius-0.9.0/tests/test_shared_helpers.py +85 -0
  54. python_mobius-0.8.4/CHANGELOG.md +0 -890
  55. python_mobius-0.8.4/PKG-INFO +0 -208
  56. python_mobius-0.8.4/README.md +0 -176
  57. python_mobius-0.8.4/documentation/00-overview.md +0 -81
  58. python_mobius-0.8.4/documentation/01-ble-transport.md +0 -67
  59. python_mobius-0.8.4/documentation/02-framing-and-crc.md +0 -82
  60. python_mobius-0.8.4/documentation/03-attributes-and-opcodes.md +0 -97
  61. python_mobius-0.8.4/documentation/04-device-identity.md +0 -81
  62. python_mobius-0.8.4/documentation/05-scenes.md +0 -137
  63. python_mobius-0.8.4/documentation/06-light-schedule.md +0 -454
  64. python_mobius-0.8.4/documentation/07-pump-schedule.md +0 -185
  65. python_mobius-0.8.4/documentation/08-manufacturer-data.md +0 -95
  66. python_mobius-0.8.4/documentation/09-thread-coap-relay.md +0 -369
  67. python_mobius-0.8.4/documentation/10-known-gaps-and-open-questions.md +0 -171
  68. python_mobius-0.8.4/documentation/11-device-status-attributes.md +0 -336
  69. python_mobius-0.8.4/documentation/12-device-identity-and-address-stability.md +0 -83
  70. python_mobius-0.8.4/documentation/13-attribute-dump.md +0 -246
  71. python_mobius-0.8.4/documentation/14-batched-attribute-reads.md +0 -112
  72. python_mobius-0.8.4/documentation/15-mob-template-format.md +0 -107
  73. python_mobius-0.8.4/src/mobius/cli.py +0 -1380
  74. python_mobius-0.8.4/src/mobius/coap.py +0 -250
  75. python_mobius-0.8.4/src/mobius/device.py +0 -4211
  76. python_mobius-0.8.4/src/mobius/device_status.py +0 -751
  77. python_mobius-0.8.4/src/mobius/discovery.py +0 -274
  78. python_mobius-0.8.4/src/mobius/dump.py +0 -377
  79. python_mobius-0.8.4/src/mobius/frame.py +0 -266
  80. python_mobius-0.8.4/src/mobius/manufacturer.py +0 -73
  81. python_mobius-0.8.4/src/mobius/mesh_address.py +0 -136
  82. python_mobius-0.8.4/src/mobius/mob.py +0 -188
  83. python_mobius-0.8.4/src/mobius/modifiers.py +0 -169
  84. python_mobius-0.8.4/src/mobius/power.py +0 -91
  85. python_mobius-0.8.4/src/mobius/pump_status.py +0 -50
  86. python_mobius-0.8.4/src/mobius/relay.py +0 -496
  87. python_mobius-0.8.4/src/mobius/scenes.py +0 -97
  88. python_mobius-0.8.4/src/mobius/schedule.py +0 -418
  89. {python_mobius-0.8.4 → python_mobius-0.9.0}/.forgejo/scripts/extract_changelog_section.py +0 -0
  90. {python_mobius-0.8.4 → python_mobius-0.9.0}/.forgejo/workflows/release.yml +0 -0
  91. {python_mobius-0.8.4 → python_mobius-0.9.0}/.gitignore +0 -0
  92. {python_mobius-0.8.4 → python_mobius-0.9.0}/LICENSE +0 -0
  93. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_attributes_batch.py +0 -0
  94. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_cli_group_targets.py +0 -0
  95. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_cli_mob_file.py +0 -0
  96. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_cli_schedule_file.py +0 -0
  97. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_connection_cleanup.py +0 -0
  98. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_device_summary.py +0 -0
  99. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_device_time.py +0 -0
  100. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_discovery_company_ids.py +0 -0
  101. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_dump.py +0 -0
  102. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_extra_physical_values.py +0 -0
  103. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_fan_thresholds.py +0 -0
  104. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_firmware_versions.py +0 -0
  105. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_frame.py +0 -0
  106. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_frame_sniffer.py +0 -0
  107. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_full_poll_batch.py +0 -0
  108. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_group_bitmap.py +0 -0
  109. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_hyperdrive_device.py +0 -0
  110. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_insolation_detection.py +0 -0
  111. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_light_intensity_branching.py +0 -0
  112. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_light_poll_batch.py +0 -0
  113. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_manufacturer.py +0 -0
  114. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_mesh_address.py +0 -0
  115. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_mesh_discovery.py +0 -0
  116. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_mesh_discovery_direct_connect.py +0 -0
  117. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_metadata_batch.py +0 -0
  118. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_modifiers.py +0 -0
  119. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_networked_thread_devices.py +0 -0
  120. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_new_diagnostic_attributes.py +0 -0
  121. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_power.py +0 -0
  122. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_pump_schedule.py +0 -0
  123. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_pump_status.py +0 -0
  124. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_pump_supported_modes.py +0 -0
  125. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_pump_telemetry.py +0 -0
  126. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_schedule_file_format.py +0 -0
  127. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_schedule_intensity_scalar.py +0 -0
  128. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_serial_decoding.py +0 -0
  129. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_serial_identity.py +0 -0
  130. {python_mobius-0.8.4 → python_mobius-0.9.0}/tests/test_supported_attributes.py +0 -0
@@ -0,0 +1,237 @@
1
+ # Changelog
2
+
3
+ ## 0.9.0
4
+
5
+ - **Breaking:** `RelayedMobiusDevice` writes now raise an error when the
6
+ device rejects them, like direct writes already did.
7
+ - Added helpers shared with ha-mobius: `enum_or_none()`,
8
+ `primitive_type_from_name()`, `support_tier()`, `parse_advertisement()`,
9
+ `schedule_capacity()`/`min_schedule_capacity()`, `format_mesh_address()`,
10
+ `pump_params_to_dict()`, `Scene.is_empty` and `PUMP_PRIMITIVES`.
11
+ - Added pump battery settings: `set_battery_backup_speed()` (limited to the
12
+ pump's battery backup max speed) and `set_boosted_battery()` (only while
13
+ running on battery), with the `--set-battery-backup-speed` and
14
+ `--set-boosted-battery-*` options.
15
+ - Added `get_operation_mode()` and the `OperationMode` enum; pump telemetry
16
+ now includes `operation_mode` (`"BatteryBackup"` while running on
17
+ battery).
18
+ - `get_full_poll_batch()` now includes the battery settings
19
+ (`battery_backup`, `boosted_battery`) and the operation mode.
20
+ - The boosted battery power is permille (0-100 %) and its on/off times are
21
+ seconds.
22
+ - `mobius-scan`: the debug mesh listing now shows peer age as unsigned, like
23
+ the library.
24
+ - **Breaking:** fixed the running scene's remaining time: it's now read from
25
+ `SceneTimer`, so it counts down, and `ActiveScene.timeout_seconds` holds
26
+ the scene's total time. `get_current_scene()` returns None when no scene
27
+ is running.
28
+ - Starting a scene, returning to the schedule and restarting all devices now
29
+ use the tank's group, like the app (`get_tank_group()`, `tank_group()`).
30
+ **Breaking:** `resume_schedule()` now ends the scene on every device of
31
+ the tank with one write (`broadcast=False` for one device only).
32
+ - **Breaking:** `set_pump_schedule()` now rejects parameters outside the
33
+ ranges the app allows (for example a Short Pulse time of 0), before
34
+ writing. Added `pump_param_range()` and `pump_param_errors()`.
35
+ - Internal cleanup: removed duplicated code and simplified comments.
36
+
37
+ ## 0.8.4
38
+
39
+ - Added `LightPollResult.lunar_enabled` and `lunar_phase_day`, valid at any
40
+ time of day (the matching `diagnostics` keys are only set at night).
41
+
42
+ ## 0.8.3
43
+
44
+ - Added `set_lunar_enabled(enabled)`: the value the app's Lunar chip
45
+ writes.
46
+
47
+ ## 0.8.2
48
+
49
+ - Added `LightPollResult.schedule_intensity`: the schedule intensity
50
+ setting (unlike `diagnostics["scalar"]`, which is the lunar reduction at
51
+ night).
52
+
53
+ ## 0.8.1
54
+
55
+ - Added `get_pump_reverse(primitive_type, params)`: whether a pump
56
+ point's speed means reverse rotation (AlpacaV1 only).
57
+ - Added `reboot_all()`: restarts every device of the tank, like the app's
58
+ "Restart all Devices".
59
+
60
+ ## 0.8.0
61
+
62
+ - Added `supported_pump_modes(primitive_type, closed_loop=None)`: the pump
63
+ modes each pump supports (see `documentation/07-pump-schedule.md`,
64
+ "Modes per pump").
65
+ - Added schedule writing: `set_light_schedule()`, `set_pump_schedule()`,
66
+ `GroupBitmap` (`group_mask`), and the `--dump-schedule-file` /
67
+ `--load-schedule-file` options.
68
+ - Added `.mob` template files: export and import for light and pump
69
+ schedules, and the `--dump-mob-file` / `--load-mob-file` options.
70
+ - Loading a schedule into a light now also writes every light in its
71
+ schedule group, after checking the group's smallest capacity.
72
+ - `get_pump_telemetry()` now includes every other sensor value the pump
73
+ reports (`extra_physical_values`).
74
+ - Added `get_extra_physical_values()`, also shown for lights in
75
+ `get_device_summary()`.
76
+ - Added `get_fan_thresholds()`.
77
+
78
+ ## 0.7.2
79
+
80
+ - `get_full_poll_batch()` now includes the scenes.
81
+ - `start_scene()` / `start_feed_mode()` now apply to the whole tank by
82
+ default; `broadcast=False` writes one device only.
83
+
84
+ ## 0.7.1
85
+
86
+ - Added scenes: `get_configured_scenes()`, `get_current_scene()`,
87
+ `start_scene()`.
88
+ - Fixed `get_current_scene()` failing on user-created scenes.
89
+ - `start_scene()`'s duration is in seconds (it was documented as minutes).
90
+
91
+ ## 0.7.0
92
+
93
+ - Added `get_full_poll_batch()`: everything a poll needs in one request,
94
+ 2-2.6× faster.
95
+ - Added `get_light_poll_batch()`: a light's schedule and intensity inputs
96
+ in one request.
97
+ - `get_current_pump_block()` accepts an already-read schedule (`points`).
98
+
99
+ ## 0.6.0
100
+
101
+ - Added `get_metadata_batch()`: settings, calibration, firmware, hardware,
102
+ channels, error state and clock in one request (6-14× faster over the
103
+ relay), with a fallback to individual reads (`used_batch`,
104
+ `force_individual_reads`).
105
+ - Added `get_attributes_batch()`: several attributes in one request.
106
+ - Added attribute dumps: `get_supported_attributes()`,
107
+ `dump_attributes()`, `enrich_attribute_dump()` and the
108
+ `--dump-attributes` / `--parse-dump` options.
109
+ - Added `set_advanced_features()` and the `--set-local-control-enabled`,
110
+ `--set-auto-dim-timeout`, `--set-max-fan-speed` and
111
+ `--set-fan-shutdown-enabled` options.
112
+ - The lunar calculation now uses the same date as the app, so the values
113
+ match what the app shows.
114
+ - Fixed `RelayedMobiusDevice` not relaying `get_supported_attributes()`
115
+ and `get_attributes_batch()`.
116
+ - Fixed `--dump-attributes` and `--set-time-to-now` ignoring
117
+ `--relay-target`.
118
+
119
+ ## 0.5.0
120
+
121
+ - `get_pump_telemetry()` now returns `gph_reliable`, `minimum_gph` and
122
+ `maximum_gph`; flow is only reliable when the pump reports a flow range.
123
+ - Added `motor_power_watts` to `get_pump_telemetry()` (Vectra, NYOS
124
+ Quantum).
125
+ - Added support for AquaIllumination devices (company ID `0x0001`).
126
+ **Breaking:** `MOBIUS_COMPANY_ID` is now `MOBIUS_COMPANY_ID_ECOTECH`.
127
+ - Added `get_lunar_phase_info()` (the device's own lunar state) and show
128
+ it in `--dump-schedule`. Read errors are now raised instead of
129
+ returning `None`.
130
+ - Added `get_advanced_features()`, `get_vectra_info()` and
131
+ `get_coffee_info()`.
132
+ - Added `set_time_to_now()` (sets the clock of the whole tank) and
133
+ `reboot()`.
134
+ - Fixed firmware labels for AquaIllumination devices.
135
+ - A relayed device that timed out now fails its other reads in the same
136
+ poll immediately.
137
+
138
+ ## 0.4.4
139
+
140
+ - The device model list is now complete (EcoTech Marine,
141
+ AquaIllumination, Neptune Systems, NYOS). `Model.VersaVX1` is now listed
142
+ under EcoTech Marine.
143
+ - Several models are now named after their product (e.g. `Model.Axis40`,
144
+ `Model.Blade`, `Model.Quantum160`).
145
+ - Fixed the lunar-dimmed light intensity disagreeing with the app for
146
+ hours at a time.
147
+
148
+ ## 0.4.3
149
+
150
+ - Relayed reads are retried once before failing, like the app does.
151
+
152
+ ## 0.4.2
153
+
154
+ - Fixed `MeshPeer.age` being read as signed. It is the time since the peer
155
+ was last heard on the mesh, so it can go down.
156
+ - Fixed `mobius-scan --dump-schedule` failing for pumps.
157
+
158
+ ## 0.4.1
159
+
160
+ - Fixed `discover_tank()` sometimes listing the connected device twice.
161
+
162
+ ## 0.4.0
163
+
164
+ - Added mesh discovery through `NetworkedThreadDevices`, which works
165
+ without an MXM hub: `discover_networked_thread_devices()`, `MeshPeer.age`
166
+ and `mesh_local_prefix_from_mlprefix()`.
167
+ - Added `discover_mesh_peers_auto()`, the recommended way to discover the
168
+ tank's devices.
169
+ - Added `Tank` and `discover_tank()`: every device of a tank with its mesh
170
+ address and age.
171
+ - `mobius-scan` now lists devices by tank and connects to one device to
172
+ show the tank's mesh addresses; `--scan-only` skips connecting,
173
+ `--full-summaries` connects to every device and `--dump-tanks` shows
174
+ every tank.
175
+
176
+ ## 0.3.1
177
+
178
+ - Fixed the Brightness channel not dimming the other channels, which
179
+ made light intensities too high.
180
+
181
+ ## 0.3.0
182
+
183
+ - Fixed lunar dimming being applied to the wrong part of the night.
184
+ - `get_hardware_info()` now returns labels for colour, product, radio and
185
+ motor type, and is included in `get_device_summary()`.
186
+
187
+ ## 0.2.1
188
+
189
+ - Fixed lists that the device returns in several parts being cut short
190
+ (for example some firmware versions were missing).
191
+
192
+ ## 0.2.0
193
+
194
+ - Added relaying through the Thread mesh: `RelayedMobiusDevice` reaches
195
+ every device of a tank through one BLE connection. Added `mobius.coap`
196
+ and `mobius.mesh_address`.
197
+ - Added mesh discovery: `get_own_mesh_address()`,
198
+ `discover_mesh_peers()` (MXM hub) and
199
+ `discover_mesh_peers_via_direct_connect()`.
200
+ - Added the `mobius-scan` options `--dump-mesh-peers`, `--relay-target`,
201
+ `--debug-relay`, `--build-peer-map` and `--dump-schedule`.
202
+ - Added `get_device_time_info()` (clock drift).
203
+ - `get_current_light_intensities()` now reports how its result was
204
+ computed in `.diagnostics`.
205
+
206
+ ## 0.1.4
207
+
208
+ - Added `MobiusDevice.is_connected`.
209
+
210
+ ## 0.1.3
211
+
212
+ - Devices are now identified by serial number instead of BLE address,
213
+ which can change: `find_device_by_serial()`, `dedupe_by_serial()`,
214
+ `MobiusDevice(serial=...)` and `mobius-scan --by-serial`.
215
+ - Added `get_firmware_versions()` and `get_hardware_info()`.
216
+ - Added `get_group_info()`, `get_calibration_info()` and
217
+ `get_maintenance_info()`.
218
+ - Added `get_pump_flow_range()`, `get_pump_override_mode()`,
219
+ `get_battery_backup_info()` and `get_boosted_battery_info()`, included
220
+ in `get_device_summary()`.
221
+ - Light intensities now include the schedule intensity, lunar phases,
222
+ acclimation and hyperdrive (`get_current_light_percentages()`), and flag
223
+ Insolation (not supported).
224
+
225
+ ## 0.1.2
226
+
227
+ - Added `manufacturer_for_model()` / `MODEL_MANUFACTURER`.
228
+ - Serial numbers are returned as text instead of hex.
229
+ - `get_pump_telemetry()` now includes `speed_percent`.
230
+ - Fixed a connection left open when subscribing to notifications failed.
231
+
232
+ ## 0.1.0
233
+
234
+ - First release: framing, CRC and attribute get/set; scenes; pump
235
+ telemetry and schedules; light schedules and current intensities;
236
+ device identity and `get_device_summary()`; discovery and tank grouping
237
+ from advertisements; the `mobius-scan` command.
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.5
2
+ Name: python-mobius
3
+ Version: 0.9.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,90 @@
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
+ | Know what is not covered or not tested | [10](./10-known-gaps-and-open-questions.md) |
31
+
32
+ ## Document map
33
+
34
+ | File | Covers |
35
+ |---|---|
36
+ | [01-ble-transport.md](./01-ble-transport.md) | GATT service and characteristics, fragmentation, connection notes |
37
+ | [02-framing-and-crc.md](./02-framing-and-crc.md) | Frame layout, opGroups, the reserved field, CRC16, message IDs, status codes |
38
+ | [03-attributes-and-opcodes.md](./03-attributes-and-opcodes.md) | Get/Set attribute, multi-element values, batched reads, listing supported attributes, group writes |
39
+ | [04-device-identity.md](./04-device-identity.md) | `PrimitiveType`, `Model`, support tiers, `ErrorState`, firmware and hardware info |
40
+ | [05-scenes.md](./05-scenes.md) | Configured scenes, starting and cancelling a scene |
41
+ | [06-light-schedule.md](./06-light-schedule.md) | Light schedule format, computing the current intensity, lunar and acclimation modifiers, hyperdrive, writing, schedule groups |
42
+ | [07-pump-schedule.md](./07-pump-schedule.md) | Pump schedule format, modes and parameters, supported modes per pump, writing |
43
+ | [08-manufacturer-data.md](./08-manufacturer-data.md) | Advertisement contents: model, serial and pan_id without connecting |
44
+ | [09-thread-coap-relay.md](./09-thread-coap-relay.md) | Thread mesh addressing, peer discovery, relaying requests through a connected device |
45
+ | [10-known-gaps-and-open-questions.md](./10-known-gaps-and-open-questions.md) | Unimplemented features, untested areas, open questions |
46
+ | [11-device-status-attributes.md](./11-device-status-attributes.md) | Pump telemetry, settings, calibration, maintenance, clock, diagnostics, commands |
47
+ | [12-device-identity-and-address-stability.md](./12-device-identity-and-address-stability.md) | Why devices are identified by serial number, not BLE address |
48
+ | [13-attribute-dump.md](./13-attribute-dump.md) | Reading every supported attribute; dump file formats |
49
+ | [14-attribute-reference.md](./14-attribute-reference.md) | Every known attribute ID and enum value |
50
+ | [15-mob-template-format.md](./15-mob-template-format.md) | The app's `.mob` schedule template file |
51
+ | [16-python-library.md](./16-python-library.md) | How this Python library maps onto the protocol; CLI |
52
+
53
+ ## Conventions
54
+
55
+ - All multi-byte integers are **little-endian** unless stated otherwise.
56
+ - `u8`/`u16`/`u32`/`u64` are unsigned, `i8`/`i16`/`i32`/`i64` signed.
57
+ - **Permille**: many percentages are sent as tenths of a percent
58
+ (0-1000 = 0.0-100.0 %).
59
+ - Times of day in schedules are **minutes since midnight** (0-1439).
60
+ - Absolute times are **Unix seconds** unless stated otherwise.
61
+ - Strings are UTF-8 (or ASCII), padded with `0x00` to their field size.
62
+ - "Attribute 500" means the attribute with ID 500; names are those used in
63
+ [14-attribute-reference.md](./14-attribute-reference.md).
64
+
65
+ ## Glossary
66
+
67
+ | Term | Meaning |
68
+ |---|---|
69
+ | FSCI / C2CI | Framing layer (FSCI) and the attribute get/set command set on top of it (C2CI) |
70
+ | Attribute | A numbered value on the device (read with Get, written with Set), optionally with several elements addressed by index |
71
+ | Element / index | One value of a multi-value attribute (a schedule point, a firmware component, ...) |
72
+ | Primitive | The device-type-specific payload of a schedule point or scene (channel intensities, pump mode and parameters) |
73
+ | Tank | The set of devices sharing one Thread mesh (same pan_id and mesh prefix) |
74
+ | Gateway | The device a client is connected to over BLE, used to reach the other devices of the tank over Thread |
75
+ | Relayed device | A device reached through the gateway instead of its own BLE connection |
76
+
77
+ ## Verification status
78
+
79
+ Statements about the protocol come from the app's behaviour and, where
80
+ noted, from tests on real hardware (VorTech MP40 pumps, Radion XR15 G6
81
+ lights, an AquaIllumination Axis pump). Parts that were never exercised on
82
+ real hardware are listed in
83
+ [10-known-gaps-and-open-questions.md](./10-known-gaps-and-open-questions.md).
84
+
85
+ ## Legal and scope
86
+
87
+ This is independent reverse engineering for interoperability with hardware
88
+ you own. No vendor code is reproduced; only the protocol facts needed to
89
+ interoperate (wire formats, constants, byte layouts). Not affiliated with
90
+ or endorsed by EcoTech Marine, AquaIllumination, Neptune Systems or NYOS.
@@ -0,0 +1,67 @@
1
+ # BLE transport
2
+
3
+ ## Service and characteristics
4
+
5
+ Every device exposes one GATT service with four characteristics:
6
+
7
+ | UUID | Name | Direction | Properties |
8
+ |---|---|---|---|
9
+ | `01ff0100-ba5e-f4ee-5ca1-eb1e5e4b1ce0` | Service | | |
10
+ | `01ff0101-ba5e-f4ee-5ca1-eb1e5e4b1ce0` | `RX_DATA` | device → client | notify: fragment |
11
+ | `01ff0102-ba5e-f4ee-5ca1-eb1e5e4b1ce0` | `RX_FINAL` | device → client | notify: last fragment |
12
+ | `01ff0103-ba5e-f4ee-5ca1-eb1e5e4b1ce0` | `TX_DATA` | client → device | write without response: fragment |
13
+ | `01ff0104-ba5e-f4ee-5ca1-eb1e5e4b1ce0` | `TX_FINAL` | client → device | write without response: last fragment |
14
+
15
+ Subscribe to notifications on both `RX_DATA` and `RX_FINAL` before sending
16
+ anything.
17
+
18
+ ## Fragmentation
19
+
20
+ A frame ([02-framing-and-crc.md](./02-framing-and-crc.md)) is split into
21
+ chunks no larger than the ATT payload size:
22
+
23
+ ```
24
+ chunks = split(frame, mtu_payload) # at least one chunk
25
+ for chunk in chunks[:-1]: write(TX_DATA, chunk)
26
+ write(TX_FINAL, chunks[-1]) # the device processes the message now
27
+ ```
28
+
29
+ Receiving is symmetric: append every `RX_DATA` notification to a buffer;
30
+ an `RX_FINAL` notification appends its bytes and completes the frame. Parse
31
+ the buffer and clear it.
32
+
33
+ 20 bytes (the BLE 4.0 default ATT payload) always works; a larger
34
+ negotiated MTU only reduces the number of writes.
35
+
36
+ ## Several requests in flight
37
+
38
+ Responses are matched to requests by message ID
39
+ ([02](./02-framing-and-crc.md#message-id)); relayed responses by CoAP token
40
+ ([09](./09-thread-coap-relay.md)). A client can therefore keep several
41
+ requests outstanding, although sending them one at a time is simpler and
42
+ is what the app does in practice.
43
+
44
+ ## Advertising
45
+
46
+ Devices advertise the local name `MOBIUS` (compare case-insensitively) and
47
+ the service UUID above. Model, serial number and pan_id are in the
48
+ manufacturer data. The manufacturer data has been observed only in scan
49
+ responses: a passive scan of a Radion saw only the name and the UUID. See
50
+ [08-manufacturer-data.md](./08-manufacturer-data.md).
51
+
52
+ ## Connection notes
53
+
54
+ These are client-side observations, not protocol rules:
55
+
56
+ - A device connected to a phone running the Mobius app usually stops
57
+ advertising and can't be connected to until the phone disconnects.
58
+ - On Linux/BlueZ the first connection attempt fails fairly often; retrying
59
+ (for example with `bleak-retry-connector`) is reliable.
60
+ - Connecting through a device object obtained from a recent scan, instead
61
+ of a bare address that triggers a new scan, avoids BlueZ forgetting the
62
+ device between scan and connect.
63
+ - The BLE address is not a stable identity; see
64
+ [12-device-identity-and-address-stability.md](./12-device-identity-and-address-stability.md).
65
+ - Holding a connection to every device leaves the app nothing to connect
66
+ to; keeping one connection per tank and relaying to the rest
67
+ ([09](./09-thread-coap-relay.md)) avoids that.