Skip to content

Commit 61f9747

Browse files
committed
feat!: 💥 rename HIDRO/ION Low Flow status keys to Low + add Redox Low (#40)
- rename decoded status key "HIDRO Low Flow" to "HIDRO Low" - rename decoded status key "ION Low Flow" to "ION Low" - bit 1 (0x0002) of MBF_HIDRO_STATUS / MBF_ION_STATUS signals the module cannot reach the configured production setpoint, not a flow problem; flow indicators live on bits 3 (FL1) and 9 (FL2) - add "Redox Low" key (bit 0x0080) to decode_ph_rx_cl_cd_status_bits, emitted only for unit=Redox; brings symmetry with HIDRO/ION Low - drop stale comment about HIDRO bit 15 (Tasmota MBMSK_HIDRO_STATUS does not define it; the decoded dict already omitted it) Resolves #33
1 parent 30a5a8e commit 61f9747

2 files changed

Lines changed: 38 additions & 6 deletions

File tree

‎src/neopool_modbus/status_mask.py‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def decode_ph_rx_cl_cd_status_bits(status: int | None, unit: str) -> dict[str, b
8585
"""Decode the status bits for pH, Redox, Chlorine, and Conductivity sensors."""
8686
# Status bits are 16 bits, where each bit represents a status flag
8787
# Bit 3: Flow sensor problem
88+
# Bit 7: Low - the module cannot reach the configured setpoint (Redox only)
8889
# Bit 10: Module control status (flow detection control)
8990
# Bit 11: Acid pump active (pH only; depends on MBF_PAR_PH_ACID_RELAY_GPIO)
9091
# Bit 12: Pump active (base pump for pH; dosing pump for Rx/CL/CD)
@@ -104,14 +105,17 @@ def decode_ph_rx_cl_cd_status_bits(status: int | None, unit: str) -> dict[str, b
104105
# Bit 11 is the acid pump - only meaningful for the pH module
105106
if unit == "pH":
106107
result[f"{unit} acid pump active"] = bool(status & 0x0800)
108+
# Bit 7 signals a low-production alarm - only defined for the Redox module
109+
if unit == "Redox":
110+
result[f"{unit} Low"] = bool(status & 0x0080)
107111
return result
108112

109113

110114
def decode_ion_status_bits(status: int | None) -> dict[str, bool]:
111115
"""Decode the status bits for ION sensor."""
112116
# Status bits are 16 bits, where each bit represents a status flag
113117
# Bit 0: ION On Target
114-
# Bit 1: ION Low Flow
118+
# Bit 1: ION Low - the ionization cannot reach the configured setpoint
115119
# Bit 2: ION Reserved
116120
# Bit 3: ION Program time exceeded
117121
# Bit 12: ION in dead time
@@ -123,7 +127,7 @@ def decode_ion_status_bits(status: int | None) -> dict[str, bool]:
123127
return {}
124128
return {
125129
"ION On Target": bool(status & 0x0001),
126-
"ION Low Flow": bool(status & 0x0002),
130+
"ION Low": bool(status & 0x0002),
127131
"ION Reserved": bool(status & 0x0004),
128132
"ION Program time exceeded": bool(status & 0x0008),
129133
"ION in dead time": bool(status & 0x1000),
@@ -136,7 +140,7 @@ def decode_hidro_status_bits(status: int | None) -> dict[str, bool]:
136140
"""Decode the status bits for HIDRO sensor."""
137141
# Status bits are 16 bits, where each bit represents a status flag
138142
# Bit 0: HIDRO On Target
139-
# Bit 1: HIDRO Low Flow
143+
# Bit 1: HIDRO Low - the hydrolysis cannot reach the configured setpoint
140144
# Bit 2: HIDRO Reserved
141145
# Bit 3: HIDRO Cell Flow FL1 (if present)
142146
# Bit 4: Pool Cover (cover input active)
@@ -149,13 +153,11 @@ def decode_hidro_status_bits(status: int | None) -> dict[str, bool]:
149153
# Bit 12: HIDRO in dead time
150154
# Bit 13: HIDRO in Pol1
151155
# Bit 14: HIDRO in Pol2
152-
# Bit 15: HIDRO measurement module detected
153-
# Note: HIDRO measurement module is always detected if HIDRO sensor is present
154156
if status is None:
155157
return {}
156158
return {
157159
"HIDRO On Target": bool(status & 0x0001),
158-
"HIDRO Low Flow": bool(status & 0x0002),
160+
"HIDRO Low": bool(status & 0x0002),
159161
"HIDRO Reserved": bool(status & 0x0004),
160162
"HIDRO Cell Flow FL1": bool(status & 0x0008), # if present
161163
"Pool Cover": bool(status & 0x0010),

‎tests/test_status_mask.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ def test_decode_ph_rx_cl_cd_status_bits_no_acid_for_non_ph():
9898
assert "Redox pump active" in result
9999

100100

101+
def test_decode_ph_rx_cl_cd_status_bits_redox_low():
102+
"""Bit 7 (0x0080) is the Redox Low alarm, only defined for Redox."""
103+
result = decode_ph_rx_cl_cd_status_bits(0x0080, "Redox")
104+
assert result["Redox Low"] is True
105+
106+
107+
@pytest.mark.parametrize("unit", ["pH", "Chlorine", "Conductivity"])
108+
def test_decode_ph_rx_cl_cd_status_bits_low_key_only_for_redox(unit):
109+
"""The Low key is emitted only for Redox, never for pH/CL/CD."""
110+
# Every bit set - if Low ever leaked into other units it would show up.
111+
result = decode_ph_rx_cl_cd_status_bits(0xFFFF, unit)
112+
assert f"{unit} Low" not in result
113+
114+
101115
def test_decode_ion_status_bits_basic():
102116
# ION On Target, ION in Pol2
103117
status = 0x4001
@@ -107,6 +121,13 @@ def test_decode_ion_status_bits_basic():
107121
assert result["ION in dead time"] is False
108122

109123

124+
def test_decode_ion_status_bits_low_bit():
125+
# Bit 1 represents low production (cannot reach setpoint), not flow.
126+
result = decode_ion_status_bits(0x0002)
127+
assert result["ION Low"] is True
128+
assert "ION Low Flow" not in result
129+
130+
110131
def test_decode_ion_status_bits_none():
111132
assert decode_ion_status_bits(None) == {}
112133

@@ -121,6 +142,15 @@ def test_decode_hidro_status_bits_basic():
121142
assert result["Pool Cover"] is False
122143

123144

145+
def test_decode_hidro_status_bits_low_bit():
146+
# Bit 1 represents low production (cannot reach setpoint), not flow.
147+
# Flow indicators live on bits 3 (FL1) and 9 (FL2).
148+
result = decode_hidro_status_bits(0x0002)
149+
assert result["HIDRO Low"] is True
150+
assert result["HIDRO Cell Flow FL1"] is False
151+
assert "HIDRO Low Flow" not in result
152+
153+
124154
def test_decode_hidro_status_bits_none():
125155
assert decode_hidro_status_bits(None) == {}
126156

0 commit comments

Comments
 (0)