From 782741ea582e36ed9fec8646bc81fdbefb4b90e4 Mon Sep 17 00:00:00 2001 From: gdluck Date: Sat, 24 Oct 2026 15:38:09 +0300 Subject: [PATCH] fix(lighting): normalize_where canonicalises area-0 four-digit points normalize_where("0004") returned "0004", an address no gateway accepts: own-firmware-oracle lights-area0 records `*#1*0001##` and `*1*1*0001##` as nack/silent on 11 of the 12 emulated gateways (F450, F453, F453AV, F454, F459, F460, F461, H4684, MH200, MH200N, MyHomeServer1), and the MH202, the only one that ACKs them, reports the light as `01` (`*1*1000#1*01##`) while it echoes a real 4-digit `0010` as `0010`. The live F461 2.0.11 NACKs `*#1*0001##` and `*1*1*0001##` within 10 ms and ACKs `*#1*01##` / `*1*1*01##`. The same rule is what the MyHOME#147 thread gives users by hand (A=0 PL=3 is "03", A=0 PL=12 is "0012"). A 4-digit base with area 00 and point 1..9 (`000N`) now normalises to `0N`, keeping any interface suffix (`0007#4#01` -> `07#4#01`). Every other 4-digit input (`0109`, `0010`, `0112`, `1001`) is left as typed, as decided in #123. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 3 +++ OWNd/message/lighting.py | 15 ++++++++++++- tests/test_who1_ts10_builders.py | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51a99e0..f682817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ All notable changes to `OWNd` are documented in this file. - Canonical `libqtdevices` (`TS10_1_0_23`) coarse `WHAT 2..10` $\leftrightarrow$ fine `1..100%` conversion helpers (`COARSE_PRESET_TO_PERCENT`, `brightness_preset_to_percent`, `percent_to_brightness_preset`), `OWNLightingEvent.is_coarse_dimmer`, `OWNLightingEvent.brightness_preset_percent`, and `OWNLightingCommand.set_brightness(..., coarse=..., gateway=...)` (`#134`). - `WHAT 39` end of movement (`*1*39*WHERE##`) from WHO 1 motion detectors: `message_type` `motion_detected` with `motion` `False`, new `OWNLightingEvent.motion_end`, `is_sensor` `True`, `is_on` `None`. It was `unknown_state` `39` before (`MyHOME#777`). +### Fixed +- **WHO 1 (`normalize_where`)**: a 4-digit point address of area 0 (`0004`, `0007#4#01`) normalises to its 2-digit form (`04`, `07#4#01`), the one gateways accept and report: `*#1*0001##` / `*1*1*0001##` are `nack`/`silent` on 11 of 12 emulated gateways (own-firmware-oracle `lights-area0`) and on a live F461 2.0.11, and the MH202 reports the light as `01`. Other 4-digit inputs (`0109`, `0010`) stay as typed (`#123`, `MyHOME#147`). + ## [2.0.0b11] - 2026-10-20 ### Added diff --git a/OWNd/message/lighting.py b/OWNd/message/lighting.py index 0c4352f..6da9cb4 100644 --- a/OWNd/message/lighting.py +++ b/OWNd/message/lighting.py @@ -157,7 +157,20 @@ def normalize_where(where: str | int | None) -> str: return "" parts = where_str.split("#", 1) base = parts[0] - if is_apl_address(base) or base in ("00", "100", "0"): + if ( + len(base) == 4 + and base.isdigit() + and base[:3] == "000" + and base[3] != "0" + ): + # Area 0, PL 1..9 spelled with four digits (0004) is canonically + # 04: `*#1*0001##` and `*1*1*0001##` are nack/silent on 11 of the 12 + # emulated gateways (own-firmware-oracle lights-area0) and the one + # that ACKs them, the MH202, reports the light as `01` + # (`*1*1000#1*01##`); a live F461 2.0.11 NACKs `0001` and ACKs `01`. + # Any other 4-digit input (0109, 0209) is left as typed (OWNd#123). + norm_base = f"{base[1]}{base[3]}" + elif is_apl_address(base) or base in ("00", "100", "0"): norm_base = base elif base.isdigit() and base.startswith("00") and int(base) > 15: norm_base = str(int(base)) diff --git a/tests/test_who1_ts10_builders.py b/tests/test_who1_ts10_builders.py index 7069ead..d9f960c 100644 --- a/tests/test_who1_ts10_builders.py +++ b/tests/test_who1_ts10_builders.py @@ -78,6 +78,42 @@ def test_apl_addressing_helpers(): assert normalize_where(" ") == "" +@pytest.mark.parametrize( + ("where", "expected"), + [ + # Area 0, PL 1..9 written on 4 digits: `*#1*0001##` is nack/silent on + # 11 of the 12 emulated gateways (own-firmware-oracle lights-area0), + # and the MH202, which ACKs it, reports the light as `01`. + ("0004", "04"), + ("0001", "01"), + ("0007#4#01", "07#4#01"), + # Any other 4-digit input is left as typed (OWNd#123). + ("0109", "0109"), + ("0209", "0209"), + ("0809", "0809"), + ("0901", "0901"), + # Real 4-digit addresses stay as written (the MH202 echoes 0010 as 0010). + ("0010", "0010"), + ("0015", "0015"), + ("0112", "0112"), + ("1015", "1015"), + ("1001", "1001"), + # Not point addresses, or already canonical. + ("0000", "0000"), + ("0900", "0900"), + ("04", "04"), + ("29", "29"), + ("00", "00"), + ("0", "0"), + ("#0004", "#0004"), + ], +) +def test_normalize_where_canonicalises_area_0_four_digit_points(where, expected): + from OWNd.message import normalize_where + + assert normalize_where(where) == expected + + def test_lighting_dimension_properties(): from OWNd.message import OWNLightingEvent, OWNMessage