From 95dc8a567f248cbe63ab9360f7b036e07f799fb4 Mon Sep 17 00:00:00 2001 From: gdluck Date: Sat, 24 Oct 2026 15:40:52 +0300 Subject: [PATCH] fix(profiles): drop WHO 16 from the F461 and MyHomeServer1 profiles Both profiles advertised WHO 16 (Sound System), so MyHOME's startup sweep sent `*#16*0*5##` and configured WHO 16 media players only ever produced refused frames on these gateways. own-firmware-oracle: all 86 WHO 16 frames of the suites (77 of them in sound-who16-source) are nack/silent on F461 020011 and MyHomeServer1 028206, i.e. no translator forwards them; the firmware table shows both images ship bt_multi (WHO 22) and no bt_difson (WHO 16), and the F461 openserver Stackopen lists no WHO 16 client. Live: the F461 2.0.11 NACKs `*#16*0*5##` within 5 ms (2026-10-23) and a MyHomeServer1 NACKs it in the MyHOME issue_466 trace fixture; MyHOME#459 already notes the MyHomeServer1 "firmware discards 5, 16, 22". WHO 22 stays listed and supports_audio is unchanged: sound-who22 is forwarded by both. No new flag: supported_who already carries the meaning. MH202 keeps WHO 16 although its oracle refuses WHO 16 commands, because a live MH202 relays WHO 16 state from an F441M (MyHOME#669) and supported_who also decides which gateway owns those events. F459 and F460 resolve to GenericGatewayProfile and are not changed. scripts/sync_documentation.py --update reports the README already in sync (the profile table does not list WHOs). Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 3 +++ OWNd/profiles.py | 28 ++++++++++++++++++++++++++-- tests/test_profiles.py | 28 +++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51a99e0..8874ba0 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 +- **Profiles (`F461Profile`, `MyHomeServer1Profile`)**: WHO 16 is no longer in `supported_who`: all 86 WHO 16 frames of the firmware oracle are `nack`/`silent` on F461 020011 and MyHomeServer1 028206 (`sound-who16-source` and the other suites), a live F461 2.0.11 NACKs `*#16*0*5##`, and the MyHomeServer1 firmware discards WHO 16 (`MyHOME#459`). WHO 22 stays listed and `supports_audio` is unchanged; MH202 keeps WHO 16. + ## [2.0.0b11] - 2026-10-20 ### Added diff --git a/OWNd/profiles.py b/OWNd/profiles.py index c255a47..edd767d 100644 --- a/OWNd/profiles.py +++ b/OWNd/profiles.py @@ -185,6 +185,17 @@ def can_support_workers(self, count: int) -> bool: who for who in DEFAULT_SUPPORTED_WHO if who != WHO_CEN_PLUS ) +# Gateways without a WHO 16 translator: the F461 020011 and MyHomeServer1 +# 028206 images ship bt_multi (WHO 22) but no bt_difson (WHO 16) +# (own-firmware-oracle README firmware table; the F461 openserver Stackopen +# lists no WHO 16 client), every WHO 16 frame of the firmware oracle (86 per +# gateway, sound-who16-source and the other suites) is nack/silent on them, a +# live F461 2.0.11 NACKs *#16*0*5## within 5 ms, and a MyHomeServer1 NACKs it +# in the MyHOME issue_466 trace fixture (MyHOME#459: "firmware discards 5, 16, +# 22"). Their sound system is WHO 22 (sound-who22), so supports_audio stays +# True and WHO 22 stays listed. +_WITHOUT_WHO16 = (WHO_SOUND,) + # Firmware at which Legrand documents CEN+ on the F453AV (WHO 25 p. 13). _F453AV_CEN_PLUS_FIRMWARE = (2, 1, 7) @@ -387,6 +398,8 @@ class F461Profile(GatewayProfile): (`020011`) does not package the ``bt_energia`` translator and strictly NACKs all WHO 3 requests without bus activity (`results/F461/020011/oracle/full/energy-ts10.tsv`). WHO 3 is therefore excluded from ``supported_who``. + - WHO 16 (Sound System): refused, the sound system is WHO 22 (see + ``_WITHOUT_WHO16``). Injection flags, from the live F461 2.0.11 capture of 2026-10-18 (MyHOME ``tests/fixtures/traces/f461_live_2026-10-18``) and the plant @@ -415,7 +428,9 @@ def __init__(self) -> None: supports_who4_command_injection=True, supports_who18_response_injection=True, supported_who=tuple( - w for w in DEFAULT_SUPPORTED_WHO if w != WHO_LOAD_CONTROL + w + for w in DEFAULT_SUPPORTED_WHO + if w != WHO_LOAD_CONTROL and w not in _WITHOUT_WHO16 ), ) @@ -532,6 +547,11 @@ class MH202Profile(GatewayProfile): *5*9*0##) and its zone states (WHAT 11/18). WHO 5 here means reading the alarm: the plant owner reports the central unit rejects SCS arm/disarm from any gateway and arms through WHO 9 AUX frames instead (see OWNAlarmCommand). + + WHO 16 stays listed although the firmware oracle (MH202 010024) refuses + every WHO 16 command: a live MH202 with an F441M matrix relays WHO 16 + state frames (``*#16*41*1*1##``, ``*#16*41*1*6##``; MyHOME#669 trace), + and ``supported_who`` also decides which gateway owns those events. """ def __init__(self) -> None: @@ -610,6 +630,8 @@ class MyHomeServer1Profile(GatewayProfile): NACKs all WHO 3 requests without bus activity (see own-firmware-oracle findings/MyHomeServer1/energy-ts10.md). WHO 3 is therefore excluded from ``supported_who``. + - WHO 16 (Sound System): refused, the sound system is WHO 22 (see + ``_WITHOUT_WHO16``). """ def __init__(self) -> None: @@ -624,7 +646,9 @@ def __init__(self) -> None: supports_native_transitions=True, supports_extended_frames=True, supported_who=tuple( - w for w in DEFAULT_SUPPORTED_WHO if w != WHO_LOAD_CONTROL + w + for w in DEFAULT_SUPPORTED_WHO + if w != WHO_LOAD_CONTROL and w not in _WITHOUT_WHO16 ), ) diff --git a/tests/test_profiles.py b/tests/test_profiles.py index 83ca9fc..9ae33f1 100644 --- a/tests/test_profiles.py +++ b/tests/test_profiles.py @@ -103,7 +103,9 @@ def test_profile_lookup_accepts_common_name_variants() -> None: assert profile.model_name == "MyHomeServer1" assert profile.supports_session_count(4) - assert profile.supports_who(WHO_SOUND) + # Firmware oracle: every WHO 16 frame is refused; sound is WHO 22. + assert not profile.supports_who(WHO_SOUND) + assert profile.supports_who(WHO_SOUND_DIFFUSION) # Firmware oracle proves MyHomeServer1 omits bt_energia and rejects WHO 3 assert not profile.supports_who(WHO_LOAD_CONTROL) @@ -651,3 +653,27 @@ def test_profile_injection_capability_flags() -> None: assert p.supports_who18_response_injection is None +@pytest.mark.parametrize("model", ["F461", "MyHomeServer1", "MHS1", "MyHome Server 1"]) +def test_who16_is_not_advertised_where_the_oracle_refuses_it(model: str) -> None: + """Every WHO 16 frame of the oracle (86 per gateway, sound-who16-source and + others) is ``nack``/``silent`` on F461 020011 and MyHomeServer1 028206: no + translator forwards it; a live F461 2.0.11 NACKs ``*#16*0*5##`` within + 5 ms and a MyHomeServer1 NACKs it in the MyHOME issue_466 trace fixture + (MyHOME#459). They forward WHO 22 (``sound-who22``), so sound stays + advertised through WHO 22 and ``supports_audio``. + """ + profile = get_gateway_profile(model) + + assert not profile.supports_who(WHO_SOUND) + assert profile.supports_who(WHO_SOUND_DIFFUSION) + assert profile.supports_audio is True + + +@pytest.mark.parametrize( + "model", ["F454", "MH200N", "MH200", "H4684", "F453", "F453AV", "H4890", "MH202"] +) +def test_who16_stays_on_gateways_that_forward_it(model: str) -> None: + """MH202: the oracle refuses WHO 16 commands, but a live MH202 relays WHO 16 + state frames from an F441M (``*#16*41*1*1##``, MyHOME#669), so it keeps + owning WHO 16 events.""" + assert get_gateway_profile(model).supports_who(WHO_SOUND)