Skip to content

Commit 468db01

Browse files
committed
feat(decoders): ✨ expose label collections and add ph alarm / filtvalve codecs (#31)
- add PH_STATUS_ALARM_LABELS + decode_ph_alarm (values 0-6; value 6 is real-hardware tank-empty state, matched by Tasmota's driver) - expose FILTRATION_MODE_LABELS, FILTRATION_SPEED_LABELS, CELL_BOOST_MODE_LABELS as public dicts (renamed from private) - add FILTVALVE_MODE_LABELS + decode_filtvalve_mode + encode_filtvalve_mode for the {1, 3, 4} user-facing subset - add FILTRATION_SPEED_STATE_LABELS, HIDRO_POLARITY_LABELS, ION_POLARITY_LABELS, PH_PUMP_STATUS_LABELS as the canonical return-value tuples of the matching decoders - add ph_pump_options helper narrowing PH_PUMP_STATUS_LABELS by MBF_PAR_RELAY_PH mode - document all new symbols in README with a lookup table - document value 6 in docs/modbus-registers.md pH alarm tables
1 parent c912eb5 commit 468db01

4 files changed

Lines changed: 394 additions & 29 deletions

File tree

README.md

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -205,21 +205,37 @@ from neopool_modbus.capabilities import (
205205
is_uv_lamp_present,
206206
)
207207
from neopool_modbus.decoders import (
208+
CELL_BOOST_MODE_LABELS,
209+
FILTRATION_MODE_LABELS,
210+
FILTRATION_SPEED_LABELS,
211+
FILTRATION_SPEED_STATE_LABELS,
212+
FILTVALVE_MODE_LABELS,
213+
HIDRO_POLARITY_LABELS,
214+
ION_POLARITY_LABELS,
215+
PH_PUMP_STATUS_LABELS,
216+
PH_STATUS_ALARM_LABELS,
208217
aggregate_filtration_remaining,
209218
build_timer_block,
210219
combine_u32,
211220
decode_cell_boost,
212221
decode_filtration_mode,
213222
decode_filtration_speed,
223+
decode_filtvalve_mode,
224+
decode_hidro_polarity,
225+
decode_ion_polarity,
214226
decode_par_model_modules,
227+
decode_ph_alarm,
228+
decode_ph_pump_status,
215229
derive_timer_stop,
216230
encode_cell_boost,
217231
encode_filtration_mode,
218232
encode_filtration_speed,
233+
encode_filtvalve_mode,
219234
get_machine_name,
220235
hhmm_to_seconds,
221236
is_hydrolysis_in_percent,
222237
parse_timer_block,
238+
ph_pump_options,
223239
seconds_to_hhmm,
224240
# ... see neopool_modbus.decoders for the full list
225241
)
@@ -233,6 +249,42 @@ from neopool_modbus.status_mask import (
233249
)
234250
```
235251

252+
### Enum labels
253+
254+
The `decoders` module publishes the label collections that pair with
255+
each enum-shaped decoder, so integrations do not have to duplicate the
256+
mappings in their own UI code:
257+
258+
| Collection | Type | Pairs with |
259+
| ------------------------------- | ----------------- | ----------------------------------------------------- |
260+
| `FILTRATION_MODE_LABELS` | `dict[int, str]` | `decode_filtration_mode` / `encode_filtration_mode` |
261+
| `FILTRATION_SPEED_LABELS` | `dict[int, str]` | `decode_filtration_speed` / `encode_filtration_speed` |
262+
| `CELL_BOOST_MODE_LABELS` | `dict[int, str]` | `decode_cell_boost` / `encode_cell_boost` |
263+
| `FILTVALVE_MODE_LABELS` | `dict[int, str]` | `decode_filtvalve_mode` / `encode_filtvalve_mode` |
264+
| `PH_STATUS_ALARM_LABELS` | `dict[int, str]` | `decode_ph_alarm` |
265+
| `FILTRATION_SPEED_STATE_LABELS` | `tuple[str, ...]` | return values of `compute_filtration_speed_state` |
266+
| `HIDRO_POLARITY_LABELS` | `tuple[str, ...]` | return values of `decode_hidro_polarity` |
267+
| `ION_POLARITY_LABELS` | `tuple[str, ...]` | return values of `decode_ion_polarity` |
268+
| `PH_PUMP_STATUS_LABELS` | `tuple[str, ...]` | return values of `decode_ph_pump_status` |
269+
270+
Dict-shaped collections use the wire value as the key; tuple-shaped
271+
ones enumerate the string outputs of a pure decoder that reads more
272+
than one register. The `ph_pump_options(data)` helper returns the
273+
subset of `PH_PUMP_STATUS_LABELS` reachable under the current
274+
`MBF_PAR_RELAY_PH` mode (acid-only / base-only / both).
275+
276+
```python
277+
from neopool_modbus.decoders import (
278+
FILTRATION_MODE_LABELS,
279+
decode_filtration_mode,
280+
ph_pump_options,
281+
)
282+
283+
modes = list(FILTRATION_MODE_LABELS.values()) # ("manual", "auto", ...)
284+
current = decode_filtration_mode(data.get("MBF_PAR_FILT_MODE"))
285+
options = ph_pump_options(data) # narrowed by MBF_PAR_RELAY_PH
286+
```
287+
236288
### Capabilities
237289

238290
`neopool_modbus.capabilities` exposes pure predicates over a register
@@ -262,16 +314,16 @@ The client exposes named operations for the writes integrations
262314
typically need, so callers do not have to reach for raw register
263315
addresses:
264316

265-
| Method | Effect |
266-
| ---------------------------------------------- | ------------------------------------------------------------------------------------- |
267-
| `async_set_filtration_mode(name, apply=True)` | manual / auto / heating / smart / intelligent / backwash |
268-
| `async_set_cell_boost(name, apply=True)` | inactive / active / active_redox |
269-
| `async_set_filtration_speed(name, apply=False)`| low / mid / high; RMW on `MBF_PAR_FILTRATION_CONF` (cache hot path, fresh-read cold path) |
270-
| `async_set_temp_setpoint(raw, apply=True)` | writes the same scaled value to heating + intelligent registers in sync |
271-
| `async_clear_errors()` | one-shot to `MBF_ESCAPE` |
272-
| `async_save_to_eeprom()` | one-shot to `MBF_SAVE_TO_EEPROM` |
273-
| `async_reset_user_counters()` | resets user counters and chains the EEPROM save (the reset is volatile) |
274-
| `async_sync_device_time(timestamp)` | writes the 32-bit `timestamp` to `MBF_PAR_TIME` and triggers `MBF_ACTION_COPY_TO_RTC` |
317+
| Method | Effect |
318+
| ----------------------------------------------- | ----------------------------------------------------------------------------------------- |
319+
| `async_set_filtration_mode(name, apply=True)` | manual / auto / heating / smart / intelligent / backwash |
320+
| `async_set_cell_boost(name, apply=True)` | inactive / active / active_redox |
321+
| `async_set_filtration_speed(name, apply=False)` | low / mid / high; RMW on `MBF_PAR_FILTRATION_CONF` (cache hot path, fresh-read cold path) |
322+
| `async_set_temp_setpoint(raw, apply=True)` | writes the same scaled value to heating + intelligent registers in sync |
323+
| `async_clear_errors()` | one-shot to `MBF_ESCAPE` |
324+
| `async_save_to_eeprom()` | one-shot to `MBF_SAVE_TO_EEPROM` |
325+
| `async_reset_user_counters()` | resets user counters and chains the EEPROM save (the reset is volatile) |
326+
| `async_sync_device_time(timestamp)` | writes the 32-bit `timestamp` to `MBF_PAR_TIME` and triggers `MBF_ACTION_COPY_TO_RTC` |
275327

276328
Unknown mode/speed names raise `ValueError` before any I/O happens.
277329

@@ -285,12 +337,12 @@ All client methods translate underlying pymodbus exceptions into the
285337
`NeoPoolError` hierarchy at the library boundary, so callers never need
286338
to import `pymodbus` to catch errors:
287339

288-
| Class | Raised when |
289-
| ------------------------ | -------------------------------------------------------------------------------- |
290-
| `NeoPoolConnectionError` | TCP connect fails, returned `False`, or the client is in its post-failure backoff |
291-
| `NeoPoolTimeoutError` | Connect, read, or write times out (`asyncio.TimeoutError`) |
340+
| Class | Raised when |
341+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
342+
| `NeoPoolConnectionError` | TCP connect fails, returned `False`, or the client is in its post-failure backoff |
343+
| `NeoPoolTimeoutError` | Connect, read, or write times out (`asyncio.TimeoutError`) |
292344
| `NeoPoolModbusError` | A read returns a Modbus exception response (`isError()` true), or `async_write_aux_relay` / one of the timer write follow-ups returns `isError()` |
293-
| `NeoPoolError` | Common base; catch this to handle any of the above |
345+
| `NeoPoolError` | Common base; catch this to handle any of the above |
294346

295347
> [!WARNING]
296348
> `NeoPoolModbusClient.async_write_register()` is the exception to the

docs/modbus-registers.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ Status of the pH control module. Bitmask:
171171
| 3 | pH pump (acid or base) exceeded the maximum working time set in `MBF_PAR_RELAY_PH_MAX_TIME` and has been stopped |
172172
| 4 | pH value is above PH1 setpoint |
173173
| 5 | pH value is below PH2 setpoint |
174+
| 6 | Acid or base tank empty (float switch tripped) |
174175

175176
**pH alarm values for acid-only regulation:**
176177

@@ -182,6 +183,7 @@ Status of the pH control module. Bitmask:
182183
| 3 | pH pump exceeded maximum working time and has been stopped |
183184
| 4 | pH value is above PH1 setpoint + 0.1 |
184185
| 5 | pH value is below PH1 setpoint - 0.3 |
186+
| 6 | Acid tank empty (float switch tripped) |
185187

186188
**pH alarm values for base-only regulation:**
187189

@@ -193,6 +195,7 @@ Status of the pH control module. Bitmask:
193195
| 3 | pH pump exceeded maximum working time and has been stopped |
194196
| 4 | pH value is above PH2 setpoint + 0.1 |
195197
| 5 | pH value is below PH2 setpoint - 0.3 |
198+
| 6 | Base tank empty (float switch tripped) |
196199

197200
---
198201

0 commit comments

Comments
 (0)