python-mobius 0.8.0__tar.gz → 0.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_mobius-0.8.0 → python_mobius-0.8.1}/CHANGELOG.md +9 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/PKG-INFO +1 -1
- {python_mobius-0.8.0 → python_mobius-0.8.1}/pyproject.toml +1 -1
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/__init__.py +3 -1
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/constants.py +29 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/device.py +22 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_device_control.py +49 -5
- python_mobius-0.8.1/tests/test_pump_reverse.py +52 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/.forgejo/scripts/extract_changelog_section.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/.forgejo/workflows/release.yml +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/.gitignore +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/LICENSE +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/README.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/00-overview.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/01-ble-transport.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/02-framing-and-crc.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/03-attributes-and-opcodes.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/04-device-identity.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/05-scenes.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/06-light-schedule.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/07-pump-schedule.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/08-manufacturer-data.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/09-thread-coap-relay.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/10-known-gaps-and-open-questions.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/11-device-status-attributes.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/12-device-identity-and-address-stability.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/13-attribute-dump.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/14-batched-attribute-reads.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/15-mob-template-format.md +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/cli.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/coap.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/crc.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/device_status.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/discovery.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/dump.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/frame.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/manufacturer.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/mesh_address.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/mob.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/modifiers.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/power.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/pump_status.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/relay.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/scenes.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/src/mobius/schedule.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_attributes_batch.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_cli_group_targets.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_cli_mob_file.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_cli_schedule_file.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_coap.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_coap_indication_handling.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_connection_cleanup.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_device_status.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_device_summary.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_device_time.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_discovery_company_ids.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_dump.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_extra_physical_values.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_fan_thresholds.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_firmware_versions.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_frame.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_frame_sniffer.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_full_poll_batch.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_group_bitmap.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_hyperdrive_device.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_insolation_detection.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_light_intensity_branching.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_light_poll_batch.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_light_schedule.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_manufacturer.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_manufacturer_lookup.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_mesh_address.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_mesh_discovery.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_mesh_discovery_direct_connect.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_metadata_batch.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_mob.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_modifiers.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_networked_thread_devices.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_new_diagnostic_attributes.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_power.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_pump_schedule.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_pump_status.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_pump_supported_modes.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_pump_telemetry.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_relay.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_scenes.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_schedule_file_format.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_schedule_intensity_scalar.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_serial_decoding.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_serial_identity.py +0 -0
- {python_mobius-0.8.0 → python_mobius-0.8.1}/tests/test_supported_attributes.py +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.1
|
|
4
|
+
|
|
5
|
+
- Added `get_pump_reverse(primitive_type, params)`: whether a pump
|
|
6
|
+
schedule point's MaxSpeed/MinSpeed value currently encodes reverse
|
|
7
|
+
rotation. Only AlpacaV1 pumps support this -- every other primitive
|
|
8
|
+
type always returns None.
|
|
9
|
+
- Added `reboot_all()`: soft-reboots every device on the mesh at
|
|
10
|
+
once, matching the app's own "Restart all Devices" tank setting.
|
|
11
|
+
|
|
3
12
|
## 0.8.0
|
|
4
13
|
|
|
5
14
|
- Added `supported_pump_modes(primitive_type, closed_loop=None)`: the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-mobius
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-mobius"
|
|
7
|
-
version = "0.8.
|
|
7
|
+
version = "0.8.1"
|
|
8
8
|
description = "Reverse-engineered Python client for the Mobius BLE protocol (EcoTech Marine VorTech/Radion, AquaIllumination, Neptune Systems, NYOS)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "GPL-2.0-only" }
|
|
@@ -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.1"
|
|
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",
|
|
@@ -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
|
|
@@ -1296,6 +1296,28 @@ class MobiusDevice:
|
|
|
1296
1296
|
"""
|
|
1297
1297
|
await self.set_attribute(C2Attribute.Reset, bytes([ResetType.Soft]))
|
|
1298
1298
|
|
|
1299
|
+
async def reboot_all(self) -> None:
|
|
1300
|
+
"""
|
|
1301
|
+
Soft-reboots every device on this mesh at once -- what the
|
|
1302
|
+
app's own "Restart all Devices" (tank settings screen) does.
|
|
1303
|
+
|
|
1304
|
+
Confirmed from the app's own SettingsFragment.reboot(): this
|
|
1305
|
+
is NOT a loop of individual reboot() calls against every
|
|
1306
|
+
device. It's the exact same single Set as reboot() (Reset=Soft),
|
|
1307
|
+
just with the frame's own reserved-byte "group" field set to 1
|
|
1308
|
+
(see make_reserved()) instead of left at 0 -- one write to
|
|
1309
|
+
this device, propagated to the rest of the mesh the same way
|
|
1310
|
+
set_time_to_now()/start_scene(broadcast=True) already do.
|
|
1311
|
+
group=1 specifically (not the app's own client-side, per-tank
|
|
1312
|
+
group number, which isn't something this library has any way
|
|
1313
|
+
to discover) matches set_time_to_now()'s own confirmed-working
|
|
1314
|
+
value, and the same real-hardware caveats apply here: not
|
|
1315
|
+
guaranteed reliable across every mesh topology or under
|
|
1316
|
+
real-world conditions where a peer is briefly unreachable when
|
|
1317
|
+
the write's own propagation happens.
|
|
1318
|
+
"""
|
|
1319
|
+
await self.set_attribute(C2Attribute.Reset, bytes([ResetType.Soft]), reserved=make_reserved(group=1))
|
|
1320
|
+
|
|
1299
1321
|
# ---- scenes -----------------------------------------------------------
|
|
1300
1322
|
|
|
1301
1323
|
async def start_scene(self, scene_id: int, duration_seconds: int = 0, broadcast: bool = True) -> None:
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Tests for device control write operations --
|
|
3
|
-
Confirmed wire format
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
hardware, direct or relayed -- see
|
|
2
|
+
Tests for device control write operations -- reboot() and
|
|
3
|
+
reboot_all(). Confirmed wire format via the app's own source, which
|
|
4
|
+
sends exactly this for its own "Restart" button and "Restart all
|
|
5
|
+
Devices" tank setting respectively. NOT confirmed: these specific
|
|
6
|
+
writes against real hardware, direct or relayed -- see each method's
|
|
7
|
+
own docstring.
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
@@ -63,6 +64,49 @@ async def test_reboot_propagates_a_write_failure():
|
|
|
63
64
|
await device.reboot()
|
|
64
65
|
|
|
65
66
|
|
|
67
|
+
@pytest.mark.asyncio
|
|
68
|
+
async def test_reboot_all_writes_reset_soft_with_group_1_reserved_byte():
|
|
69
|
+
"""Confirmed via the app's own SettingsFragment.reboot() (the
|
|
70
|
+
"Restart all Devices" tank-settings option): the exact same Reset
|
|
71
|
+
Set as reboot(), but with the frame's own reserved-byte group
|
|
72
|
+
field set to 1 instead of left ungrouped -- one write to this
|
|
73
|
+
device propagates to the rest of the mesh, matching
|
|
74
|
+
set_time_to_now()'s own already-confirmed group=1 behavior."""
|
|
75
|
+
device = MobiusDevice(_fake_ble_device())
|
|
76
|
+
captured = {}
|
|
77
|
+
|
|
78
|
+
async def fake_set_attribute(attr_id, value, index=0, reserved=b"\x00\x00"):
|
|
79
|
+
captured["attr_id"] = attr_id
|
|
80
|
+
captured["value"] = value
|
|
81
|
+
captured["reserved"] = reserved
|
|
82
|
+
|
|
83
|
+
device.set_attribute = fake_set_attribute
|
|
84
|
+
await device.reboot_all()
|
|
85
|
+
|
|
86
|
+
assert captured["attr_id"] == C2Attribute.Reset
|
|
87
|
+
assert captured["value"] == bytes([ResetType.Soft])
|
|
88
|
+
assert captured["reserved"] == make_reserved(group=1)
|
|
89
|
+
# Distinct from reboot()'s own ungrouped reserved bytes -- this is
|
|
90
|
+
# the whole point of the two methods being separate.
|
|
91
|
+
assert captured["reserved"] != b"\x00\x00"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.asyncio
|
|
95
|
+
async def test_reboot_all_propagates_a_write_failure():
|
|
96
|
+
"""Same reasoning as reboot()'s own equivalent test -- a caller
|
|
97
|
+
acting on a silently-swallowed failure here would wrongly believe
|
|
98
|
+
every device on the mesh actually rebooted."""
|
|
99
|
+
device = MobiusDevice(_fake_ble_device())
|
|
100
|
+
|
|
101
|
+
async def fake_set_attribute(attr_id, value, index=0, reserved=b"\x00\x00"):
|
|
102
|
+
raise IOError("device returned FSCI status Failed setting attribute 6")
|
|
103
|
+
|
|
104
|
+
device.set_attribute = fake_set_attribute
|
|
105
|
+
|
|
106
|
+
with pytest.raises(IOError, match="Failed setting attribute 6"):
|
|
107
|
+
await device.reboot_all()
|
|
108
|
+
|
|
109
|
+
|
|
66
110
|
# --------------------------------------------------------------------------
|
|
67
111
|
# set_attribute_multi() -- writing several elements of one attribute in
|
|
68
112
|
# a single request (used by set_light_schedule()/set_pump_schedule()).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for get_pump_reverse() -- ported from the decompiled app's own
|
|
3
|
+
PumpPrimitive.getReverse(). Confirmed against the app's own
|
|
4
|
+
PumpParameterEntry.sliderSettings(): MaxSpeed and MinSpeed only ever
|
|
5
|
+
set supportsReverse=true when primitive_type is AlpacaV1 -- every
|
|
6
|
+
other primitive type never supports reverse rotation at all,
|
|
7
|
+
regardless of mode or the value's own sign.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from mobius import PrimitiveType, PumpParam, get_pump_reverse
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_alpacav1_negative_max_speed_is_reverse():
|
|
14
|
+
assert get_pump_reverse(PrimitiveType.AlpacaV1, {PumpParam.MaxSpeed: -420}) is True
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_alpacav1_positive_max_speed_is_not_reverse():
|
|
18
|
+
assert get_pump_reverse(PrimitiveType.AlpacaV1, {PumpParam.MaxSpeed: 420}) is False
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_alpacav1_negative_min_speed_is_reverse():
|
|
22
|
+
assert get_pump_reverse(PrimitiveType.AlpacaV1, {PumpParam.MinSpeed: -100}) is True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_alpacav1_checks_max_speed_before_min_speed_when_both_present():
|
|
26
|
+
# Matches the app's own getReverse() -- it stops at the first
|
|
27
|
+
# param on the primitive whose slider settings support reverse,
|
|
28
|
+
# which for AlpacaV1 is MaxSpeed before MinSpeed in params() order.
|
|
29
|
+
assert get_pump_reverse(PrimitiveType.AlpacaV1, {PumpParam.MaxSpeed: -420, PumpParam.MinSpeed: 100}) is True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_non_alpaca_primitive_types_never_support_reverse_even_with_a_negative_value():
|
|
33
|
+
"""The genuinely important case -- confirmed straight from the
|
|
34
|
+
app's own sliderSettings(): a negative value on any other
|
|
35
|
+
primitive type isn't a real, meaningful reverse-rotation signal,
|
|
36
|
+
since the concept doesn't apply there at all."""
|
|
37
|
+
for primitive in (
|
|
38
|
+
PrimitiveType.VorTechV1,
|
|
39
|
+
PrimitiveType.PumpV1,
|
|
40
|
+
PrimitiveType.TurtleV1,
|
|
41
|
+
PrimitiveType.CoffeeV1,
|
|
42
|
+
PrimitiveType.VectraV1,
|
|
43
|
+
):
|
|
44
|
+
assert get_pump_reverse(primitive, {PumpParam.MaxSpeed: -420}) is None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_returns_none_when_neither_speed_param_is_present():
|
|
48
|
+
assert get_pump_reverse(PrimitiveType.AlpacaV1, {PumpParam.PhaseShift: 180}) is None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_returns_none_for_an_empty_params_dict():
|
|
52
|
+
assert get_pump_reverse(PrimitiveType.AlpacaV1, {}) is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_mobius-0.8.0 → python_mobius-0.8.1}/documentation/10-known-gaps-and-open-questions.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|