python-mobius 0.8.0__py3-none-any.whl → 0.8.2__py3-none-any.whl
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.
- mobius/__init__.py +3 -1
- mobius/constants.py +29 -0
- mobius/device.py +48 -2
- {python_mobius-0.8.0.dist-info → python_mobius-0.8.2.dist-info}/METADATA +1 -1
- {python_mobius-0.8.0.dist-info → python_mobius-0.8.2.dist-info}/RECORD +8 -8
- {python_mobius-0.8.0.dist-info → python_mobius-0.8.2.dist-info}/WHEEL +0 -0
- {python_mobius-0.8.0.dist-info → python_mobius-0.8.2.dist-info}/entry_points.txt +0 -0
- {python_mobius-0.8.0.dist-info → python_mobius-0.8.2.dist-info}/licenses/LICENSE +0 -0
mobius/__init__.py
CHANGED
|
@@ -28,6 +28,7 @@ from .constants import (
|
|
|
28
28
|
SceneID, OperationState, FsciStatus,
|
|
29
29
|
PumpMode, PumpOverrideMode, RampType, PumpParam, PUMP_PARAM_SIZE, PUMP_MODE_PARAMS,
|
|
30
30
|
supported_pump_modes,
|
|
31
|
+
get_pump_reverse,
|
|
31
32
|
FirmwareType, FIRMWARE_TYPE_LABELS_ETM, HardwareInfo,
|
|
32
33
|
Color, COLOR_LABELS, RadioType, RADIO_TYPE_LABELS,
|
|
33
34
|
MotorType, MOTOR_TYPE_LABELS, ProductType, PRODUCT_TYPE_LABELS,
|
|
@@ -81,7 +82,7 @@ from .mob import (
|
|
|
81
82
|
MAX_PUMP_PRIMITIVE_SIZE,
|
|
82
83
|
)
|
|
83
84
|
|
|
84
|
-
__version__ = "0.8.
|
|
85
|
+
__version__ = "0.8.2"
|
|
85
86
|
|
|
86
87
|
__all__ = [
|
|
87
88
|
"__version__",
|
|
@@ -103,6 +104,7 @@ __all__ = [
|
|
|
103
104
|
"SceneID", "OperationState", "FsciStatus",
|
|
104
105
|
"PumpMode", "PumpOverrideMode", "RampType", "PumpParam", "PUMP_PARAM_SIZE", "PUMP_MODE_PARAMS",
|
|
105
106
|
"supported_pump_modes",
|
|
107
|
+
"get_pump_reverse",
|
|
106
108
|
"FirmwareType", "FIRMWARE_TYPE_LABELS_ETM", "HardwareInfo",
|
|
107
109
|
"Color", "COLOR_LABELS", "RadioType", "RADIO_TYPE_LABELS",
|
|
108
110
|
"MotorType", "MOTOR_TYPE_LABELS", "ProductType", "PRODUCT_TYPE_LABELS",
|
mobius/constants.py
CHANGED
|
@@ -819,6 +819,35 @@ def supported_pump_modes(
|
|
|
819
819
|
return _PUMP_SUPPORTED_MODES.get(primitive_type, [])
|
|
820
820
|
|
|
821
821
|
|
|
822
|
+
def get_pump_reverse(primitive_type: PrimitiveType, params: dict[PumpParam, int]) -> bool | None:
|
|
823
|
+
"""Whether this point's own MaxSpeed/MinSpeed value currently
|
|
824
|
+
encodes reverse rotation -- ported from the decompiled app's own
|
|
825
|
+
PumpPrimitive.getReverse(), which checks each of a primitive's own
|
|
826
|
+
params for one whose slider settings say supportsReverse, then
|
|
827
|
+
reports whether that param's raw (unabs'd) value is negative.
|
|
828
|
+
|
|
829
|
+
Confirmed directly from the app's own PumpParameterEntry.sliderSettings():
|
|
830
|
+
MaxSpeed and MinSpeed both only ever set supportsReverse=true when
|
|
831
|
+
primitive_type is AlpacaV1 -- every other primitive type
|
|
832
|
+
(VorTechV1, PumpV1, TurtleV1, CoffeeV1, VectraV1) never supports
|
|
833
|
+
reverse at all, regardless of mode or the value's own sign. This
|
|
834
|
+
always returns None for those, same as the app's own getReverse()
|
|
835
|
+
falling through to null when no param on the primitive supports
|
|
836
|
+
it.
|
|
837
|
+
|
|
838
|
+
parse()/schedule_to_dict() already report MaxSpeed/MinSpeed as
|
|
839
|
+
signed ints as-is (see PumpPrimitiveValue.parse()'s own comment),
|
|
840
|
+
so this just checks their sign -- it doesn't change what those
|
|
841
|
+
functions themselves return.
|
|
842
|
+
"""
|
|
843
|
+
if primitive_type != PrimitiveType.AlpacaV1:
|
|
844
|
+
return None
|
|
845
|
+
for param in (PumpParam.MaxSpeed, PumpParam.MinSpeed):
|
|
846
|
+
if param in params:
|
|
847
|
+
return params[param] < 0
|
|
848
|
+
return None
|
|
849
|
+
|
|
850
|
+
|
|
822
851
|
class RampType(IntEnum):
|
|
823
852
|
Sinusoidal = 1
|
|
824
853
|
Logarithmic = 2
|
mobius/device.py
CHANGED
|
@@ -145,6 +145,24 @@ class LightPollResult:
|
|
|
145
145
|
schedule_points: list[SchedulePoint]
|
|
146
146
|
intensities: LightIntensityResult
|
|
147
147
|
used_batch: bool = True
|
|
148
|
+
# The same raw 0.0-1.0 schedule-level master dimmer
|
|
149
|
+
# get_schedule_intensity() itself returns -- already fetched and
|
|
150
|
+
# consumed internally by both this method and the individual-reads
|
|
151
|
+
# fallback below (it's one of process_light_intensities()'s own
|
|
152
|
+
# required inputs), but never actually propagated out to the
|
|
153
|
+
# caller until this field existed. NOT the same thing as
|
|
154
|
+
# intensities.diagnostics["scalar"]: that reflects whichever value
|
|
155
|
+
# is CURRENTLY driving the effective per-channel output at this
|
|
156
|
+
# exact moment (which, during a lunar-reduced night segment, is
|
|
157
|
+
# the lunar reduction factor instead) -- this field is always the
|
|
158
|
+
# device's own raw schedule-level intensity setting itself,
|
|
159
|
+
# regardless of whether it's the thing actually in effect right
|
|
160
|
+
# now. A caller wanting "what would the user's own intensity
|
|
161
|
+
# slider currently show" wants this field specifically, not
|
|
162
|
+
# diagnostics["scalar"]. None only if the underlying read itself
|
|
163
|
+
# failed and decode_schedule_intensity() had truly nothing to fall
|
|
164
|
+
# back to.
|
|
165
|
+
schedule_intensity: Optional[float] = None
|
|
148
166
|
|
|
149
167
|
|
|
150
168
|
@dataclass
|
|
@@ -545,7 +563,9 @@ def decode_light_poll_from_batch(
|
|
|
545
563
|
lunar_enabled, lunar_date, lunar_date_source,
|
|
546
564
|
schedule_intensity, acclimation_info, insolation_active,
|
|
547
565
|
)
|
|
548
|
-
return LightPollResult(
|
|
566
|
+
return LightPollResult(
|
|
567
|
+
schedule_points=points, intensities=intensities, used_batch=True, schedule_intensity=schedule_intensity,
|
|
568
|
+
)
|
|
549
569
|
|
|
550
570
|
|
|
551
571
|
def decode_pump_flow_range(min_raw: list[bytes], max_raw: list[bytes]) -> Optional[PumpFlowRange]:
|
|
@@ -1296,6 +1316,28 @@ class MobiusDevice:
|
|
|
1296
1316
|
"""
|
|
1297
1317
|
await self.set_attribute(C2Attribute.Reset, bytes([ResetType.Soft]))
|
|
1298
1318
|
|
|
1319
|
+
async def reboot_all(self) -> None:
|
|
1320
|
+
"""
|
|
1321
|
+
Soft-reboots every device on this mesh at once -- what the
|
|
1322
|
+
app's own "Restart all Devices" (tank settings screen) does.
|
|
1323
|
+
|
|
1324
|
+
Confirmed from the app's own SettingsFragment.reboot(): this
|
|
1325
|
+
is NOT a loop of individual reboot() calls against every
|
|
1326
|
+
device. It's the exact same single Set as reboot() (Reset=Soft),
|
|
1327
|
+
just with the frame's own reserved-byte "group" field set to 1
|
|
1328
|
+
(see make_reserved()) instead of left at 0 -- one write to
|
|
1329
|
+
this device, propagated to the rest of the mesh the same way
|
|
1330
|
+
set_time_to_now()/start_scene(broadcast=True) already do.
|
|
1331
|
+
group=1 specifically (not the app's own client-side, per-tank
|
|
1332
|
+
group number, which isn't something this library has any way
|
|
1333
|
+
to discover) matches set_time_to_now()'s own confirmed-working
|
|
1334
|
+
value, and the same real-hardware caveats apply here: not
|
|
1335
|
+
guaranteed reliable across every mesh topology or under
|
|
1336
|
+
real-world conditions where a peer is briefly unreachable when
|
|
1337
|
+
the write's own propagation happens.
|
|
1338
|
+
"""
|
|
1339
|
+
await self.set_attribute(C2Attribute.Reset, bytes([ResetType.Soft]), reserved=make_reserved(group=1))
|
|
1340
|
+
|
|
1299
1341
|
# ---- scenes -----------------------------------------------------------
|
|
1300
1342
|
|
|
1301
1343
|
async def start_scene(self, scene_id: int, duration_seconds: int = 0, broadcast: bool = True) -> None:
|
|
@@ -3768,7 +3810,11 @@ class MobiusDevice:
|
|
|
3768
3810
|
"""
|
|
3769
3811
|
points = await self.get_light_schedule(which)
|
|
3770
3812
|
intensities = await self.get_current_light_intensities(which, minute_of_day, now)
|
|
3771
|
-
|
|
3813
|
+
schedule_intensity = await self.get_schedule_intensity(which)
|
|
3814
|
+
return LightPollResult(
|
|
3815
|
+
schedule_points=points, intensities=intensities, used_batch=False,
|
|
3816
|
+
schedule_intensity=schedule_intensity,
|
|
3817
|
+
)
|
|
3772
3818
|
|
|
3773
3819
|
async def get_full_poll_batch(
|
|
3774
3820
|
self, primitive: PrimitiveType, model: Optional[Model] = None,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-mobius
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: Reverse-engineered Python client for the Mobius BLE protocol (EcoTech Marine VorTech/Radion, AquaIllumination, Neptune Systems, NYOS)
|
|
5
5
|
Project-URL: Homepage, https://code.r3pek.org/r3pek/python-mobius
|
|
6
6
|
Project-URL: Documentation, https://code.r3pek.org/r3pek/python-mobius/src/branch/main/documentation
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
mobius/__init__.py,sha256=
|
|
1
|
+
mobius/__init__.py,sha256=yON2rPOxmGrhuAQ753NNSXkaUWFuUCc3R5JZTbRkvlY,7620
|
|
2
2
|
mobius/cli.py,sha256=DJNKcBB7VCROZ90QmFWeCkRzPCRfoTaeTZaCLJ41s-Y,79735
|
|
3
3
|
mobius/coap.py,sha256=ZRBy8ivgl1o2cVLjIp-df8Jg34c58K0_1Oec1P-gQI4,10464
|
|
4
|
-
mobius/constants.py,sha256=
|
|
4
|
+
mobius/constants.py,sha256=OlbC0GSPT3Lp7rGfIqSMkAqY535oNNRRoOd15TTVt7k,46286
|
|
5
5
|
mobius/crc.py,sha256=T6PWTp3gAHu-C05ua2LH8z8leD8mSjizM7nuC9G8KhU,2643
|
|
6
|
-
mobius/device.py,sha256=
|
|
6
|
+
mobius/device.py,sha256=Ti6jBr_gXDXXM2SlqLYV471p1tIhdDk4NTiBTWNXxPY,201501
|
|
7
7
|
mobius/device_status.py,sha256=tKZwi12fS3PVl7F-Pr5tvTNOTAttaOtAWom7ahl0LZM,33080
|
|
8
8
|
mobius/discovery.py,sha256=-N_RK8ZK91Ep-XePm9cHJa44yKiBL_Ind9wFHi_TZ-8,11742
|
|
9
9
|
mobius/dump.py,sha256=2uy-bq8Ja2W3D4dz9VboSO_KqYQ4bE8xmVxYTxWOjXg,16525
|
|
@@ -17,8 +17,8 @@ mobius/pump_status.py,sha256=qLprWM0gAoltZj7w8ykdXHPfBDMl-QnBOwrmBlvknIA,1536
|
|
|
17
17
|
mobius/relay.py,sha256=BmUc1l0mDOt9isTGs7wCh0NxehbMxfaTzm3cYs7VucA,26445
|
|
18
18
|
mobius/scenes.py,sha256=Wt0Ys2x1YjF8MNDEd6kxWqYBIYPdBSbfzXD-L5ZKzag,3592
|
|
19
19
|
mobius/schedule.py,sha256=BDm8Uh8O85AlAZuR7i7pgqKC2GNXcV_HikFoG6_3Xgo,17590
|
|
20
|
-
python_mobius-0.8.
|
|
21
|
-
python_mobius-0.8.
|
|
22
|
-
python_mobius-0.8.
|
|
23
|
-
python_mobius-0.8.
|
|
24
|
-
python_mobius-0.8.
|
|
20
|
+
python_mobius-0.8.2.dist-info/METADATA,sha256=w2YtMRx_vpnctt1CYM-VEw-uDomBvXawtGpgl_BFC6Q,9899
|
|
21
|
+
python_mobius-0.8.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
22
|
+
python_mobius-0.8.2.dist-info/entry_points.txt,sha256=Q2hlcek-bWm70zvd12v32essflPgC2su-dKDHAqbPoE,48
|
|
23
|
+
python_mobius-0.8.2.dist-info/licenses/LICENSE,sha256=7a72Msu2Q-TnoiFxemxEGkwafJGObk1W3rw9hzmyM_Y,17984
|
|
24
|
+
python_mobius-0.8.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|