Skip to content

fix(gateway): WHO 13 clock echoes with timezone 0530/124 no longer raise - #144

Draft
gdluck wants to merge 1 commit into
OpenWebNet-HA:masterfrom
gdluck:fix/who13-clock-echo-timezones
Draft

gdluck wants to merge 1 commit into
OpenWebNet-HA:masterfrom
gdluck:fix/who13-clock-echo-timezones

Conversation

@gdluck

@gdluck gdluck commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Requested by A

Summary

*#13**#0*14*30*00*0530## and ...*124## are clock writes whose timezone value is not the documented SHH form: a four-digit value with a sign digit (0530) and a three-digit negative offset of 24 h or more (124). Most gateways ACK such writes and echo them on the event session, and the F450 reports the stored 0530 on later reads. _gateway_timezone raised ValueError: Invalid gateway timezone: '0530' on the first and, for 124, datetime.timezone refused a 24 h offset; the exception propagated out of the event-session parse.

What the values mean is not verified by any capture (an ACK and an echo prove that the gateway stores the value, not what it means), so this PR decodes them as unknown (timezone is None), never as local time (""), and keeps raw_timezone as sent. Split out of #138.

Changes

  • OWNd/message/gateway.py: _UNMAPPED_TIMEZONE_REGEX = [01][0-9]{3}|1(?:2[4-9]|[3-9][0-9]), the two classes the measured values belong to, bounded by the sign digit and the length (0000..1999) and by the first negative offset datetime.timezone cannot hold (124..199; 123 is still -23:00). _gateway_timezone returns None for them (type now str | None); the four fromisoformat call sites use self._timezone or "", so the parsed time / datetime are naive. raw_timezone keeps the value; is_timezone_unconfigured stays False (that is the 999 sentinel). The timezone property docstring says what None means. This covers both the echo of a write (OWNGatewayCommand, *#13**#0*...## and *#13**#22*...##) and the read reply (OWNGatewayEvent, dimensions 0 and 22).
  • Everything else still raises as before: 200, 099, 024, 2, 12, 2000, 9999, 2400, 05300, 00530. MyHOME's tests/test_gateway_regressions.py:41 (test_malformed_gateway_clock_fields_are_rejected) requires *#13**0*15*00*00*200## and ...*099## to keep raising; they do.
  • CHANGELOG.md: entry under [Unreleased] / Fixed.

MyHOME does not read OWNGatewayEvent.timezone or raw_timezone (custom_components/myhome at v2-phase1-architecture: no consumer), so None reaches no integration code today; a future consumer gets "unknown", not "local".

State Diagram

stateDiagram-v2
    direction TB

    state "_gateway_timezone(values[3]) for *#13**0*HH*MM*SS*T## and the #0 / #22 / 22 forms" as TZ {
        [*] --> Classify
        Classify --> Local : or 999\ntimezone , unchanged
        Classify --> Offset : SHH, S in 0/1, HH 00..23\n+02 -00 / -23 -00 , unchanged
        Classify --> Unknown : 0000..1999 , sign + three digits, 0530 measured\nor 124..199 ,24 h or more, 124 measured\ntimezone None, raw_timezone kept, naive time , new - before - ValueError out of the event session
        Classify --> Rejected : 200, 099, 024, 2000, 05300, ...\nValueError , unchanged
    }
Loading

Where are the traces

No physical trace applies: 0530 and 124 were written only by the oracle harness. The live F461 2.0.11 and MyHomeServer1 3.87.13 report time zone 999 (*#13**0*...*999##, trace PR OpenWebNet-HA/MyHOME#799, merged), which parsed before and after.

What does the Oracle say

Suite who13-clock, results/<P>/<V>/oracle/full/who13-clock.tsv:

Frame ack / out (echoed on the event session) ack / silent nack / silent - / silent
*#13**#0*14*30*00*0530## F450 020010, F453 010100, F453AV 030014, F460 020012, F461 020011, H4684 020054, MH200 020102, MH200N 010108, MH202 010024 (9) none F454 020051, F459 020105 MyHomeServer1 028206
*#13**#0*14*30*00*124## the same nine plus F459 020105 (10) F454 020051 (ACK, no echo) none MyHomeServer1 028206
*#13**#22*14*30*00*0530*03*21*10*2026## the same nine (9) MyHomeServer1 028206 F454, F459 none

F453, F453AV, H4684, MH200 and MH200N also put the clock on the bus (bus: columns). Suite who13-clock-readback (findings/who13-clock.md, "Read-back after a write"): after *#13**#0*14*30*00*0530## the F450 reports T=0530 on later reads, "the only firmware that echoes a non-standard offset back"; F454 and F459 keep 102 because they NACKed 0530. So both the echo path (OWNGatewayCommand) and the reply path (OWNGatewayEvent) need the tolerance. No row says what the gateway does with the value, hence None.

Testing

  • tests/test_who13_clock_echo_timezones.py (new, 32 cases): the #0 and #22 echoes with 0530 / 124 parse as OWNGatewayCommand with timezone is None, raw_timezone as sent, is_timezone_unconfigured False, a naive 14:30:00 and the date kept; the dimension 0 and 22 replies with 0530 / 124 parse as OWNGatewayEvent the same way; five unmeasured members of the two classes (0000, 1999, 1240, 125, 199) are None too; eleven values outside them still raise (200, 099, 024, 2, 12, 2000, 9999, 2400, 05300, 00530); whole-hour offsets 000..123 still decode (123 is -23:00); 999 and empty still mean local time.
  • Fail before / pass after: on master 14 fail, 18 pass (0530 raises Invalid gateway timezone; 124 raises the timedelta range error); on this branch all 32 pass.
  • Full suite: 1939 passed, 1 skipped (master: 1907 passed, 1 skipped); 100 % statement coverage, mypy clean (str | None end to end), ruff clean, scripts/sync_documentation.py --check and scripts/verify_library_standards.py pass.

Live retest (2026-10-24)

Not exercised live: both plants report 999; no 0530 / 124 was written to a real gateway (clock writes on a real home were out of scope).

Companion

None. Split from #138 (#-prefixed dimension 14 setpoints).

🤖 Generated with Claude Code

own-firmware-oracle who13-clock: `*OpenWebNet-HA#13**#0*14*30*00*0530##` is ACKed and
echoed on the event session by nine gateways (F454, F459 NACK;
MyHomeServer1 silent), `…*124##` by eleven, and the F450 reports the
stored `0530` on later reads (who13-clock-readback). `_gateway_timezone`
raised ValueError on both and the exception left the event session.

The two measured values define two bounded classes, a four-digit value
with a sign digit and a three-digit negative offset of 24 h or more.
Their layout and meaning are unverified, so `timezone` is None (unknown),
never "" (local time), `raw_timezone` keeps the value, and the time /
datetime are naive. `200`, `099`, `2000`, `05300` and the like still
raise (MyHOME tests/test_gateway_regressions.py pins `099` and `200`).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants