Skip to content

fix(gateway): WHO 13 value fields are ASCII digits only; docs: conformance-matrix read form and oracle suites, Dimension 10/11/12 request caveats, #Z#C wording, F461 and H/L4684 golden replies (Encyclopedia audit, group 7) - #161

Merged
GreenGrassBlueOcean merged 17 commits into
OpenWebNet-HA:masterfrom
gdluck:fix/docs-and-who13-lows
Nov 12, 2026

Conversation

@gdluck

@gdluck gdluck commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Requested by A

Summary

One narrow parser hardening for WHO 13, documentation fixes, and six golden records from committed MyHOME captures. Every ASCII frame decodes exactly as before.

Fixed (runtime)

  • OWNGatewayEvent: value fields are ASCII digits only. str.isdigit and int accept fullwidth digits, so *#13**12*0*3*80*1*214*135## decoded a MAC address, *#13**19*1*2*3*4## an uptime and *#13**15*23## an Unknown (23) device type; such frames now stay untyped events with the raw frame as their log, as the Dimension 27 allowlist already did (_is_ascii_digits). A fullwidth digit in a clock field (*#13**1*03*26*10*2026##, *#13**0*12*00*00*001##, the Dimension 22 form) raised a ValueError out of date() / fromisoformat; it now raises the same ValueError("Invalid gateway date" / "Invalid gateway timezone") as any other malformed clock value, which the sessions catch (fix(gateway): WHO 13 clock echoes with timezone 0530/124 no longer raise #144; _gateway_timezone matches with re.ASCII). The review's empty-token cases (*#13**1**01*01*2026##, *#13**22*...*001**01*...##, *#13**12*0**80*...##, *#13**15*##, *#13**19**0*0*0##) never raised on master (no weekday is decoded; Dimensions 12 / 19 are guarded) and are pinned as such in tests/test_who13_ascii_values.py.

Documentation only

  • docs/protocol_conformance_matrix.md: the WHO 13 examples are the published empty-WHERE read form *#13**0## / *#13**22## (Encyclopedia who-13-integration-gateway/dimensions.md "Read form"), the form OWNGatewayCommand.request_time() and the keepalive emit; own-firmware-oracle ownd-audit-frames, who13-clock and who13-clock-readback have all thirteen emulated gateways answering them with ACK and the time, while the old *#13*0*0## is nack, silent on all thirteen (ownd-audit-frames).
  • OWNGatewayCommand.request_ip_address / request_netmask / request_mac_address: docstring caveat from who13-identification: answered by 8 of 13, ACKed silently by F453, F453AV and MH200N, NACKed with a worker crash by H4684 020054 and MH200 020102. Not refused at the send layer.
  • OWNHeatingCommand.actuators_status: #Z#C is zone Z through a four-zone central unit C (a controlled-zone address, not an actuator; feat(heating): OWNHeatingCommand.actuators_status(where) builder for *#4*WHERE*20## #140 leftover).
  • OWNGatewayEvent.device_type / WHO13_DEVICE_TYPES: an unlisted Dimension 15 code stays Unknown (N); the non-unique 200 is a list of sharers, told apart by WHO 1013 Dimension 1.
  • tests/golden/frames/who13_gateway.yaml (+6, community-plant-capture): F461 2.0.11 *#13**0*17*45*24*999##, *#13**16*2*0*11##, *#13**23*5*10*35##, *#13**24*2*26*4## (MyHOME#865, f461_live_2026-11-04 sweep), *#13**22*22*59*16*999*01*26*10*2026## (f461_who13_clock_tz_live_2026-10-26, line 309) and H/L4684 6.0.11 *#13**15*23## (issue_466/config_entry-myhome_HL4684_fw6_0_11.json); corpus regenerated (172 frames), golden README count 172.
  • tests/test_message_protocol.py: a regression test pinning the "Gateway broadcasting ..." log text MyHOME relies on.

Review follow-up (round 2, 2026-11-10)

# Item Action
1 Title / body; 999 attribution; golden frames Rewritten. The 999 datetime is the physical F461 (f461_who13_clock_tz_live_2026-10-26 line 309), not MH200N (001) or MH202 (002). The six frames are now golden records.
2 master / CHANGELOG (D14) origin/master (1cb2dc1) merged; the documentation bullet is gone; the ### Fixed entry covers the ASCII hardening only.
3 Matrix: private path, 12 vs 13 Replaced by the suite names and "all thirteen".
4 Dimension 10 / 11 / 12 builders Docstring caveats added; nothing refused.
5 Fail-closed parser bugs Cases 1-4 do not reproduce on master (code and probe: no weekday decoding, Dimensions 12 / 19 guarded, *#13**15*## reads Unknown ()); the non-ASCII half of each case does, and is fixed with _is_ascii_digits / re.ASCII. Clock fields keep raising ValueError, by #144.

Review follow-up (round 3, 2026-11-12)

# Item Action
1 "please rebase" (the branch conflicted with master after #157, #159 and #160 landed) origin/master c546a2e merged (fa54e90, one conflict: the golden README fixture count, #159's 166 against this PR's 172); corpus.json regenerated (172, byte-identical to the merge result). #159's test test_golden_corpus_dimension_10_write_is_conflict_and_no_tilt pins that count literally, so the pin moves from 166 to 172 (a8573f2). No other file differs from the round-2 head.

Testing

🤖 Generated with Claude Code

gdluck and others added 5 commits November 3, 2026 09:40
…uators_status

report 21 G5 and the OpenWebNet-HA#140 leftover.

docs/protocol_conformance_matrix.md showed `*OpenWebNet-HA#13*0*0##` / `*OpenWebNet-HA#13*0*22##`
(WHERE 0) as the verified WHO 13 examples. The published read form has an
empty WHERE, `*OpenWebNet-HA#13**DIMENSION##` (Encyclopedia
who-13-integration-gateway/dimensions.md "Read form", capabilities.md
"Command-session behavior"); the review-session oracle run of 2026-11-03
(audit_ency/ORACLE_CONFIRM.md row G5) has `*OpenWebNet-HA#13*0*0##` nack, silent on all
twelve emulated gateways and `*OpenWebNet-HA#13**0##` answered on all twelve, and the
builders (`request_time`, the event keepalive) already emit the empty form.

OWNHeatingCommand.actuators_status called `#Z#N` "an actuator addressed
through the central unit"; per who-4-temperature-control/addressing.md
"Historical central-unit variants" the form is `#Z#C`, zone Z through a
four-zone central unit C. Wording only; the accepted grammar is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ekday, tz 100 and log wording

report 21 G2, G3, G4, G6, G7, G8, G9, G10. G1 not changed: settled by
OWNd#144 (closed 2026-10-28), master keeps raising ValueError for
out-of-table clock values and the sessions catch it.

G2 / G3: Dimension 12 decodes six decimal octets in 0..255 only
(Encyclopedia who-13-integration-gateway/dimensions.md "DIMENSION 12 -
MAC address"); an octet above 255 or an eight-value reply (the ZigBee
interface IEEE address, zigbee-network-management.md "DIMENSION 12 -
Interface IEEE address", ENCY_CONFIRM ZB-108) leaves mac_address None
with the values raw in dimension_value.

G4 / G10: a Dimension 1 / 22 year must be four digits (dimensions.md
"DIMENSION 1 - Date"): `*OpenWebNet-HA#13**1*04*15*10*26##` stays a valid message as
on master, with date None instead of 0026-10-15. New `weekday` property,
0..6 from `00..06`, None outside, date unchanged and no calendar
cross-check (the TS10 client writes a fixed `00`, ENCY_CONFIRM G10).

G7: `T` 100 (GMT-0) reads `+00:00` like `000`.

G6: a parsed write frame `*OpenWebNet-HA#13**#0/michnovka#1/OpenWebNet-HA#22*...##` logs "Setting gateway
time/date to: ..." (dimensions.md "Write form": client -> gateway
writes, ENCY_CONFIRM G6); the reply form built by report_* keeps
"broadcasting".

G8: an addressed Dimension 16 reply (`*OpenWebNet-HA#13*123400#9*16*1*2*3##`) logs
"Product 123400#9's firmware version" (zigbee-network-management.md
"DIMENSION 16 and 17", ENCY_CONFIRM ZB-109/110); firmware_version and
where are unchanged.

G9: device_type docstring and the WHO13_DEVICE_TYPES comment say that an
unlisted code stays `Unknown (N)` and that the non-unique 200 is a list
of sharers, not a product identity (capabilities.md "Product
identification", dimensions.md "DIMENSION 15 - Device type").

Tests: tests/test_message_protocol.py (tz 100 and 0530 still raising,
year / weekday, write-frame logs, MAC range, eight-value dimension 12,
addressed dimension 16). The pinned-raise tests of
tests/test_message_edges.py and test_malformed_gateway_clock_is_rejected
are untouched. Golden fixtures unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nly WHO 13 helpers (decisions D59, D12, D14)

report 21 G2, G3 (precedent gate). MyHOME#804 pins that OWNGatewayEvent.mac_address formats
any six values without a 0..255 range check (tests/test_config_flow_identify.py: an octet 256
reads "100" and the config flow refuses it itself), so mac_address keeps that behaviour and the
range-checked value moves to the new valid_mac_address property (six decimal ASCII octets in
0..255, Encyclopedia who-13-integration-gateway/dimensions.md "DIMENSION 12 - MAC address";
None for the eight-value ZigBee interface IEEE address of zigbee-network-management.md).
_gateway_weekday, _gateway_date and the octet check take ASCII digits only (OWNd#139 rule:
non-ASCII digits never decode). The docs-only CHANGELOG bullet is dropped (OWNd#143: no
CHANGELOG entry when no runtime behaviour changed).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ock writes (MyHOME pin)

report 21 G6 (precedent gate). MyHOME pins the log of the write form *OpenWebNet-HA#13**#0*22*50*00*## as
"Gateway broadcasting internal time: 22:50:00."
(tests/test_f461_who13_clock_tz_live_2026_10_26_trace.py::test_written_strings_fail_before_pass_after),
so the "Setting gateway ... to" wording is not applied to parsed frames; the set_* builders keep
their own "Setting gateway ..." text as on master. CHANGELOG bullet trimmed accordingly.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…w follow-up)

report 21 G4. _gateway_date validated day / month only for a four-digit year, so
*OpenWebNet-HA#13**1*04*99*32*26## parsed with date None where master raised on month 99 / day 32
(OWNd#144: out-of-table clock values keep raising inside parse). Day and month are now
validated first exactly as on master (against a leap year, so 29 February passes), for
dimension 1 and dimension 22 on both OWNGatewayEvent and OWNGatewayCommand; only a
well-formed date with a non-four-digit year gives date None (Encyclopedia
who-13-integration-gateway/dimensions.md "DIMENSION 1 - Date"). Tests cover both orders.

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!

…s H4684 and MH200

The firmware oracle shows *OpenWebNet-HA#13**10##, *OpenWebNet-HA#13**11## and *OpenWebNet-HA#13**12## (IP address,
netmask, MAC) as nack/crash on H4684 020054 and MH200 020102 and as ack/out
on the other ten emulated gateways (findings/who13-identification.md). OWNd
builds no request for them; the decoders only read replies seen on the bus.
No send-layer guard is added on purpose: the frames are legitimate on most
gateways, so a refusal would need the gateway model at send time. Recorded in
gateway.py and the CHANGELOG (2026-11-04 audit G1).

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

gdluck commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

One documentation commit here from the 2026-11-04 cross-check: gateway.py now records, next to the Dimension 10 / 11 / 12 decoders, that requesting the IP address, netmask and MAC (*#13**10##, *#13**11##, *#13**12##) makes the gateway daemon exit on H4684 020054 and MH200 020102 in the firmware oracle (nack/crash; firmware_observed under QEMU emulation, not confirmed on physical hardware; findings/who13-identification.md), while the other eleven emulated gateways ACK them, eight with a telegram on the bus and three silently (F453, F453AV, MH200N). OWNd builds no request for them and deliberately applies no send-layer guard: the frames are legitimate on most gateways, so a refusal would need the gateway model at send time. CHANGELOG line added.

@gdluck

gdluck commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Live on the F461 2.0.11 test plant (2026-11-04), supporting the note that only H4684 / MH200 crash on WHO 13 dims 10/11/12: the F461 answers all of them without crashing (network values redacted) —

*#13**10## -> *#13**10*<IP octets>##       (the gateway's own IPv4)
*#13**11## -> *#13**11*<netmask octets>##
*#13**12## -> *#13**12*<MAC octets>##
*#13**15## -> *#13**15*200##
*#13**16## -> *#13**16*2*0*11##            (firmware 2.0.11)
*#13**27## -> *#13**27*<ASCII codes>##      ("F461")

So dims 10/11/12 are safe on this gateway, consistent with the oracle rows; the crash is H4684/MH200-specific. (These dims echo the gateway's live network interface, which is also why a committed oracle TSV had to be aligned with CI's jail interface rather than a capture host's.)

@gdluck
gdluck marked this pull request as ready for review November 4, 2026 13:46

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching the *#13*0*0## conformance-matrix error (confirmed NACK (*#*0##), SILENT across all 12/12 emulated gateways in lookup_firmware_verdict) and the #Z#C docstring leftover from #140!

Under our standing rule — if we don't have real, committed on-wire plant traces from physical hardware, we don't change behaviour — the documentation and conformance-matrix fixes here are spot-on and ready to merge, whereas the OWNd/message/gateway.py runtime changes are doc-derived without real plant captures and should be reverted so we can merge the doc fixes cleanly.

What can stay (ready to merge once trimmed)

  1. docs/protocol_conformance_matrix.md (G5): Updating the WHO 13 read examples from *#13*0*0## / *#13*0*22## (refused 12/12 in the firmware oracle) to *#13**0## / *#13**22## (answered 12/12).
  2. OWNd/message/heating.py (#140 leftover): Fixing the OWNHeatingCommand.actuators_status docstring so #Z#C is accurately described as zone Z via 4-zone central unit C.
  3. OWNd/message/gateway.py docstrings / comments (G9): Clarifying the device_type docstring and WHO13_DEVICE_TYPES comment.

Blockers — Behaviour changes without committed plant traces (please revert)

# Symbol / File Behaviour change without real plant trace Required action
1 _gateway_date (DIMENSION 1 & 22) in OWNd/message/gateway.py Changes 2-digit years (*#13**1*04*15*10*26##, *#13**22*...*26##, and write frames) from returning a datetime.date to returning None, driven solely by dimensions.md field-width prose with zero real gateway traces showing malformed 2-digit year emissions requiring a decoder change. Revert _gateway_date runtime behaviour to master.
2 _gateway_timezone ("100") in OWNd/message/gateway.py Changes timezone code "100" from "-00:00" to "+00:00" purely from dimensions.md (ownkb:claim:c001701 / c001702) with zero on-wire captures showing a gateway emitting "100" — indeed, your own live F461 2.0.11 check showed real hardware emits "999" (unconfigured), not "100". (Also note: please avoid citing uncommitted local reports/data/live_f461_allprs_2026-11-04/ paths; any live capture worth referencing should be committed as a fixture.) Revert _gateway_timezone "100" decoding to master.
3 DIMENSION 12 8-octet ZigBee log, addressed DIMENSION 16 log, & valid_mac_address / weekday in OWNd/message/gateway.py Rewrites human_readable_log on 8-value DIMENSION 12 (Gateway's IEEE address is: ...) and addressed DIMENSION 16 (Product X firmware version is: ...), and adds valid_mac_address / weekday properties with zero ZigBee (ownkb:caution:k000009) or addressed-product WHO 13 plant traces. Revert the gateway.py runtime decoding, log, and property changes (keeping only the G9 docstring/comment clarifications) so this PR merges cleanly as a docs + conformance-matrix fix.

gdluck and others added 4 commits November 4, 2026 16:46
…ces; keep the conformance-matrix and docstring fixes

Maintainer review of OpenWebNet-HA#161: documentation and conformance-matrix fixes
stay; runtime decoding, log strings and new properties derived from the
documentation alone are reverted to master, since no committed on-wire
plant traces back them.

Blocker 1 (_gateway_date, Dimensions 1 and 22): reverted to master. A
two-digit year (*OpenWebNet-HA#13**1*04*15*10*26##, *OpenWebNet-HA#13**22*...*26##, and the write
frames) reads a datetime.date / datetime again instead of None; the
_gateway_date helper is gone.

Blocker 2 (_gateway_timezone): reverted to master. T "100" reads
"-00:00" again, not "+00:00"; the added docstring is gone.

Blocker 3: reverted the eight-value Dimension 12 "ZigBee interface IEEE
address" log, the addressed Dimension 16 "Product X's firmware version"
log, the valid_mac_address and weekday properties (event and command),
the _gateway_weekday helper, the Dimension 12 octet range check and the
Dimension 10/11/12 crash comment. Dimension 12 decoding is master's
again (unchecked six-octet formatting, "Gateway's MAC address is" log).

Kept: docs/protocol_conformance_matrix.md WHO 13 read examples
*OpenWebNet-HA#13**0## / *OpenWebNet-HA#13**22## (G5); the OWNHeatingCommand.actuators_status
docstring describing #Z#C as zone Z through a four-zone central unit C;
the OWNGatewayEvent.device_type docstring and the WHO13_DEVICE_TYPES
comment clarifications (G9).

Tests: removed the eight tests pinning the reverted behaviour from
tests/test_message_protocol.py; kept
test_gateway_parsed_clock_frames_keep_the_broadcasting_log, which pins
master's unchanged log text. CHANGELOG [Unreleased]: the five WHO 13
"Changed" entries are replaced by one documentation-only entry that
claims no behaviour change.

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

gdluck commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Applied in 4b5b8a2 (pushed; the description now carries the same follow-up table at the top).

Blocker Action taken
1. _gateway_date Reverted: 2-digit years on DIMENSION 1 / 22 (event and command) return datetime.date / datetime as on master.
2. _gateway_timezone Reverted: code 100 reads -00:00 as on master.
3. Logs and properties The 8-octet ZigBee IEEE log, the addressed DIMENSION 16 "Product X" log, valid_mac_address, weekday and their helpers removed; DIMENSION 12 is master's unchecked formatting. gateway.py is master plus the G9 device_type docstring and WHO13_DEVICE_TYPES comment; the 8 tests of reverted behaviour pruned from test_message_protocol.py, the one pinning master's unchanged clock log kept.

Kept, as listed under "What can stay": the conformance-matrix WHO 13 read examples *#13**0## / *#13**22## (G5), the OWNHeatingCommand.actuators_status #Z#C docstring fix and the G9 clarifications. The reports/data/... citation that was in this description is gone (it referred to an uncommitted live sweep). CHANGELOG: one "documentation only, no behaviour change" entry. Nothing at runtime differs from master.

Committed plant evidence. The WHO 13 replies physical gateways actually send are already in MyHOME's fixtures and all carry four-digit years, so the reverted 2-digit branch is exercised by no plant: F461 2.0.11 *#13**1*04*15*10*2026## (f461_live_2026-10-15/, -18/, -23/), MH200N / MH202 / MH200 *#13**22*22*59*16*999*01*26*10*2026## with the 999 timezone field (issue_466/myhome_trace_MH200N_all_2026-09-25T19-50-04.json, issue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.json), F461 / MyHomeServer1 *#13**12*0*3*80*0*4*97## (f461_live_2026-10-23/, myhomeserver1_live_2026-10-24/), F454 / MH201 *#13**16*2*0*11## (issue_466/myhome_sweep_F454_all_2026-09-26T16-59-13.json, issue_466/myhome_sweep_MH201_all_2026-10-27T07-28-09.json). If you want them as OWNd golden records (parser shape only, no decoding change), say so and they come as a separate PR.


Update (2026-11-04, 16:07Z): New committed plant trace for WHO 13: MyHOME#865 (tests/fixtures/traces/f461_live_2026-11-04/, full custom_components.myhome DEBUG log and the bus-monitor sweep export of the physical F461 2.0.11, captured on the released OWNd 2.0.0b13, SHA-256 pinned by tests/test_f461_live_2026_11_04_trace.py). The F461 answers dimensions 0, 1, 10, 11, 12, 15, 16, 17, 19, 22, 23, 24, 27 and WHO 1013: *#13**1*03*04*11*2026## and *#13**22*17*42*27*999*03*04*11*2026## (four-digit year with weekday, time zone 999), *#13**0*17*45*24*999##, *#13**17*4*2*0##, *#13**19*0*14*53*9##, *#13**27*70#52#54#49##, *#1013**1*134*15*5*0##. It confirms that no physical F461 reply exercises the 2-digit-year branch reverted here, and that the 999 time zone field is what the plant sends. If golden records for these replies are wanted (parser shape only), they come as a separate PR.

@gdluck
gdluck marked this pull request as draft November 4, 2026 15:14
@gdluck
gdluck marked this pull request as ready for review November 4, 2026 16:45

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reverting the speculative WHO 13 runtime changes in 4b5b8a2. Looking at what remains in 95e99ee, the current diff is purely documentation + one log-regression test, which fixes two real doc errata (*#13**0## / *#13**22## in docs/protocol_conformance_matrix.md and the #Z#C docstring leftover from #140 on OWNHeatingCommand.actuators_status).

Before this can land, please address the following:

1. Rewrite the PR title and PR description so they reflect what the PR is now about

We don't care about the past iterations or what was reverted along the way — the PR title and body (## Summary, ## Changes, ## Testing, ## Precedent check) still describe six reverted runtime changes (valid_mac_address, weekday, 4-digit year enforcement in _gateway_date, 100 -> +00:00 in _gateway_timezone, 8-octet ZigBee Dimension 12 log, and addressed Dimension 16 log). That makes the PR confusing to read and will corrupt the squash-merge commit message on master.

  • Please rewrite both the PR title and the entire PR description from scratch so they state only what this PR actually changes today (no changelog of reverted commits/features).
  • In the "Committed plant evidence" section, fix the misattributed trace citation: *#13**22*22*59*16*999*01*26*10*2026## (999 timezone) comes from the physical F461 capture f461_who13_clock_tz_live_2026-10-26/myhome_trace_F461_who13_clock_tz_live_2026-10-26.json:309, not MH200N (001, already in who13_gateway.yaml:116) or MH202 (002, *#13**22*20*54*00*002*06*26*09*2026## in issue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.json:73).
  • If you cite the new F461 (f461_live_2026-11-04/myhome_sweep_F461_all_2026-11-04T15-45-56.json) and HL4684 (issue_466/config_entry-myhome_HL4684_fw6_0_11.json) captures (*#13**0*17*45*24*999##, *#13**16*2*0*11##, *#13**23*5*10*35##, *#13**24*2*26*4##, *#13**15*23##), please actually add those frames to tests/golden/frames/who13_gateway.yaml.

2. Rebase onto origin/master and fix CHANGELOG.md (D14 / OWNd#143)

  • The branch is currently 35 commits behind origin/master.
  • Per D14 (OWNd#143 — and as your own commit 3e682a6 noted), CHANGELOG.md only records user-visible runtime changes. Drop the - **Documentation only, no behaviour change**: ... (2026-11-04 audit G5, G9) bullet (or, if you include the fail-closed parser fixes in item 5 below, record only those runtime fixes and strip internal worksheet tags like (2026-11-04 audit G5, G9)).

3. docs/protocol_conformance_matrix.md: remove private local path & fix gateway count (12 -> 13)

On line 39:

(review-session oracle run of 2026-11-03, audit_ency/ORACLE_CONFIRM.md row G5; *#13**0## is answered with ACK and the current time on all twelve)

  • audit_ency/ORACLE_CONFIRM.md row G5 is a private local scratch file — cite the public own-firmware-oracle suites (ownd-audit-frames, who13-clock, who13-clock-readback) instead.
  • own-firmware-oracle runs those suites across thirteen emulated gateways (3488 030205 included), not twelve: all 13 return NACK (*#*0##), SILENT on *#13*0*0##, and all 13 return ACK (*#*1##) + clock reply on *#13**0## and *#13**22##.

4. Dimensions 10 / 11 / 12 request builders (request_ip_address, request_netmask, request_mac_address)

In #issuecomment-6619565077 you wrote that "OWNd builds no request for them", overlooking OWNGatewayCommand.request_ip_address() (*#13**10##), request_netmask() (*#13**11##), and request_mac_address() (*#13**12##) in OWNd/message/gateway.py:621-633.

  • Do not refuse them at the send layer (8/13 emulated gateways and physical F461 / MyHomeServer1 answer them, and F453, F453AV, MH200N ACK + SILENT), but please add a one-line docstring caveat on request_ip_address(), request_netmask(), and request_mac_address() noting that H4684 020054 and MH200 020102 NACK and crash their worker process in who13-identification.

5. Real WHO 13 fail-closed parser bugs on master that this audit missed

Because OWNMessage._DIMENSION_REQUEST_REPLY and _DIMENSION_WRITING (OWNd/message/base.py:102-110) match (?P<dimension_value>(?:\*\d*)+) (which permits empty tokens "" between stars as well as non-ASCII Unicode \d) and OWNEvent.parse / OWNCommand.parse do not catch ValueError, _parse_dimension in OWNd/message/gateway.py currently crashes OWNMessage.parse() with an uncaught ValueError on malformed wire frames:

  1. Dimensions 1 / #1 (gateway.py:208-210) & 22 / #22 (gateway.py:255-259): _gateway_date() checks _is_ascii_digits and returns None when the weekday token is empty (*#13**1**01*01*2026## or *#13**22*12*00*00*001**01*01*2026##), and then the very next line unconditionally runs weekday = int(self._dimension_values[0]) (or [4]), raising ValueError: invalid literal for int() with base 10: ''.
  2. Dimension 12 (gateway.py:224-228): f"{int(val):02x}" for val in self._dimension_values raises ValueError if any token is empty (*#13**12*0**80*1*214*135##) and accepts non-ASCII digits.
  3. Dimension 15 (gateway.py:231-233): if len(self._dimension_values) >= 1: dtype = int(self._dimension_values[0]) raises ValueError on *#13**15*## (where self._dimension_values == [""]) and accepts non-ASCII digits.
  4. Dimension 19 (gateway.py:240-245): int(self._dimension_values[0..3]) raises ValueError if any of the first four tokens is empty (*#13**19**0*0*0##) and accepts non-ASCII digits.
  5. _gateway_timezone (gateway.py:89): re.fullmatch(r"[01]\d{2}", value) omits re.ASCII, so fullwidth Unicode digits like "0\uff10\uff11" match and parse as "+01:00" instead of returning None (unlike _gateway_time and _gateway_date, which enforce _is_ascii_digits).

Hardening those five spots with _is_ascii_digits (leaving existing attributes and log messages on valid frames untouched) would actually fix a real fail-closed parser issue in WHO 13 on par with #155–#160.

Comment thread CHANGELOG.md Outdated
## [Unreleased]

### Changed
- **Documentation only, no behaviour change**: `docs/protocol_conformance_matrix.md` shows the WHO 13 reads in the published empty-`WHERE` form (`*#13**0##`, `*#13**22##`) instead of `*#13*0*0##` / `*#13*0*22##`, the form `OWNGatewayCommand.request_time()` and the keepalive already emit; the `OWNHeatingCommand.actuators_status` docstring describes `#Z#C` as zone `Z` through a four-zone central unit `C` (a controlled-zone address, not an actuator); the `OWNGatewayEvent.device_type` docstring and the `WHO13_DEVICE_TYPES` comment say that an unlisted Dimension 15 code stays `Unknown (N)` and that the non-unique `200` is a list of sharers, not a product identity (2026-11-04 audit G5, G9).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per D14 (OWNd#143), CHANGELOG.md only records user-visible runtime changes. Drop this Documentation only, no behaviour change entry (and avoid internal audit worksheet labels like (2026-11-04 audit G5, G9)). If you fix the fail-closed empty-token / ASCII-digit crashes in OWNd/message/gateway.py, record only that runtime fix under ### Fixed.

Comment thread docs/protocol_conformance_matrix.md Outdated
| **25** | **CEN+ / Dry Contacts**| Short press; start, hold and release of an extended press; WHERE `2` + object 0–2047 | `*25*21#1*21##`, `*25*22#1*21##`, `*25*23#1*21##`, `*25*24#1*21##` | Valid | Matched | **VERIFIED (100%)** |

Sources for the examples changed in [OWNd#77](https://github.com/OpenWebNet-HA/OWNd/issues/77). The MyHomeServer1 firmware, run on an emulated bus, produces a bus frame for each of them and refuses the examples they replace. The WHO 15 press and short release (`*15*01*73##`, `*15*01#1*73##`) were captured on an MH200N + MyHomeServer1 bus in [MyHOME#541](https://github.com/OpenWebNet-HA/MyHOME/issues/541). Long release and extended hold (`#2`, `#3`) rest on the firmware alone. The WHO 25 examples are an MH201 capture from [MyHOME#418](https://github.com/OpenWebNet-HA/MyHOME/issues/418). The energy request `*#18*51*52#26#9##` was accepted by a real MyHomeServer1.
Sources for the examples changed in [OWNd#77](https://github.com/OpenWebNet-HA/OWNd/issues/77). The MyHomeServer1 firmware, run on an emulated bus, produces a bus frame for each of them and refuses the examples they replace. The WHO 15 press and short release (`*15*01*73##`, `*15*01#1*73##`) were captured on an MH200N + MyHomeServer1 bus in [MyHOME#541](https://github.com/OpenWebNet-HA/MyHOME/issues/541). Long release and extended hold (`#2`, `#3`) rest on the firmware alone. The WHO 25 examples are an MH201 capture from [MyHOME#418](https://github.com/OpenWebNet-HA/MyHOME/issues/418). The energy request `*#18*51*52#26#9##` was accepted by a real MyHomeServer1. The WHO 13 reads use the published empty-`WHERE` form `*#13**DIMENSION##` (Encyclopedia who-13-integration-gateway/dimensions.md "Read form"): on the twelve emulated gateways `*#13**0##` is answered with `*#13**0*H*M*S*T##` by all twelve, while the `WHERE 0` spelling `*#13*0*0##` this table used to show is `nack, silent` on all twelve (review-session oracle run of 2026-11-03, `audit_ency/ORACLE_CONFIRM.md` row G5; `OWNGatewayCommand.request_time()` and the event keepalive emit `*#13**0##`).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Replace the private local file reference (review-session oracle run of 2026-11-03, audit_ency/ORACLE_CONFIRM.md row G5; ...) with the public own-firmware-oracle suites (ownd-audit-frames, who13-clock).
  2. own-firmware-oracle replays these across thirteen emulated gateways (3488 030205 included), not twelve — all 13 return NACK, SILENT on *#13*0*0## and all 13 return ACK + the current time on *#13**0## and *#13**22##.

gdluck and others added 2 commits November 11, 2026 00:13
…13-gateway counts in the matrix; Dimension 10/11/12 request caveats; F461 and H/L4684 golden replies

Review round 2 on OpenWebNet-HA#161 (GreenGrassBlueOcean, 2026-11-10):

- gateway.py: Dimensions 1, 12, 15 and 19 read their value fields only when
  they are ASCII digits (_is_ascii_digits); fullwidth digits used to decode a
  MAC, an uptime or an Unknown (..) device type, or raise from date() /
  fromisoformat. _gateway_timezone matches with re.ASCII so a fullwidth
  timezone raises the same ValueError as any malformed clock value (OpenWebNet-HA#144).
  The review's empty-token cases (dims 1 / 22 weekday, 12, 15, 19) never raised
  on master; pinned by tests/test_who13_ascii_values.py.
- request_ip_address / request_netmask / request_mac_address docstrings note
  the who13-identification verdicts (8 of 13 answer; F453, F453AV, MH200N ACK
  silently; H4684 020054 and MH200 020102 NACK and crash their worker).
- docs/protocol_conformance_matrix.md cites own-firmware-oracle by suite
  (ownd-audit-frames, who13-clock, who13-clock-readback) and the 13-gateway
  counts; the private worksheet path is gone.
- CHANGELOG: the documentation bullet is gone (D14); a Fixed entry covers the
  ASCII hardening only.
- Golden: six community-plant-capture records (F461 2.0.11 Dimensions 0 and
  22 with the 999 timezone field, 16, 23, 24; H/L4684 6.0.11 Dimension 15
  code 23) from MyHOME's committed fixtures; corpus 172 frames; README count.
- origin/master (1cb2dc1) merged in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gdluck gdluck changed the title fix(gateway): WHO 13 valid MAC, four-digit year, weekday, timezone 100, product firmware log; conformance matrix read form; #Z#C wording (Encyclopedia audit, group 7) fix(gateway): WHO 13 value fields are ASCII digits only; docs: conformance-matrix read form and oracle suites, Dimension 10/11/12 request caveats, #Z#C wording, F461 and H/L4684 golden replies (Encyclopedia audit, group 7) Nov 10, 2026
@gdluck

gdluck commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Round 2 applied in e501d6a, with three small follow-up commits for the 100 % coverage gate (6eca707, fb5568d, 4b941a9): the Dimension 22 ASCII check is covered with eight-value frames, the Dimension 19 timedelta guard is kept for absurd magnitudes and covered, and the Dimension 12 except the ASCII gate made unreachable is gone. Local coverage of OWNd/message/gateway.py is 100 %.

# Item Action taken
1 Title / body / 999 attribution / golden frames Rewritten. The 999 datetime is the F461 capture (f461_who13_clock_tz_live_2026-10-26 line 309); MH200N and MH202 carry 001 / 002. The five F461 / H/L4684 frames plus that Dimension 22 reply are golden records now (who13_gateway.yaml +6, community-plant-capture, corpus 172).
2 master / CHANGELOG Merged; the documentation bullet is gone (D14). The ### Fixed entry records only the ASCII hardening below, no worksheet tags.
3 Matrix The private path is gone; the sentence cites ownd-audit-frames, who13-clock, who13-clock-readback and says all thirteen (ACK + time on *#13**0## / *#13**22##, nack, silent on *#13*0*0##).
4 request_ip_address / request_netmask / request_mac_address Docstring caveat: 8 of 13 answer, F453 / F453AV / MH200N ACK silently, H4684 020054 and MH200 020102 NACK and crash their worker (who13-identification). Not refused at the send layer.
5 Parser Checked each case on master with the code and a parse probe. The empty-token halves (1-4) do not raise: no weekday is decoded on Dimensions 1 / 22, Dimension 12 is in a try, Dimension 19 is guarded by all(...), *#13**15*## reads Unknown () (there is no _gateway_date / _is_ascii_digits / weekday = int(...) on master; those lines were in this PR's reverted commits). The non-ASCII halves are real: fullwidth digits decoded a MAC / uptime / device type and raised out of date() / fromisoformat for clock fields. Fixed: Dimensions 1, 12, 15, 19 read ASCII digits only (12 / 15 / 19 stay untyped with the raw log; a clock field raises the same ValueError("Invalid gateway ...") as any malformed clock value, which the sessions catch, #144), _gateway_timezone uses re.ASCII. tests/test_who13_ascii_values.py (14 cases) pins both halves; every ASCII frame decodes as before.

Checks: ruff, mypy OWNd tests/typing, verify_library_standards.py, sync_documentation.py --check, validate_corpus.py (172); pytest 3119 passed; MyHOME v2-phase1-architecture f19adc57 suite with this branch on PYTHONPATH: 5601 passed, 1 deselected (the known test_check_all_documentation_real_tree pin signal). Note: #159 pins the golden README count at 166 and this PR at 172; whichever lands second needs the number bumped.

gdluck and others added 3 commits November 11, 2026 00:53
… except

The coverage gate (100 % on OWNd/) flagged gateway.py 200-201 (an except that
ASCII-gated int() can never reach) and 247 / 456 (the Dimension 22 raise, which
the tests missed with seven-value frames). The test frames now carry the eight
Dimension 22 values with a fullwidth hour, year or day.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…nd cover it

tests/test_message_exhaustive.py feeds *OpenWebNet-HA#13**19*99999999999999999999*0*0*0##:
int() of ASCII digits is fine but timedelta raises on the magnitude, so the
except stays (OverflowError, ValueError) and a test covers it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With six ASCII-digit fields int() cannot raise; the empty-token case that used
to hit this except now leaves the frame untyped before the branch.

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

Copy link
Copy Markdown
Contributor

@gdluck please rebase

gdluck and others added 2 commits November 12, 2026 14:57
…lows

Golden README fixture count: master's 166 (OpenWebNet-HA#159) plus the six WHO 13 records of this PR = 172; corpus.json regenerated with tools/golden/validate_corpus.py (172 frames).

Co-Authored-By: Claude <noreply@anthropic.com>
 landed

OpenWebNet-HA#159 (merged in c546a2e's history) pins the golden README count and the corpus length at 166 in test_golden_corpus_dimension_10_write_is_conflict_and_no_tilt; this PR adds six WHO 13 records, so the pin becomes 172 (tests/golden/README.md and corpus.json already say 172).

Co-Authored-By: Claude <noreply@anthropic.com>
@gdluck

gdluck commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

origin/master (c546a2e, after #157, #159 and #160) is merged into the branch in fa54e90 (merge commit, no history rewrite), with one follow-up commit a8573f2.

# Item Action taken
1 Rebase master merged in; the only conflict was the golden README fixture count (#159's 166 against this PR's 172). tests/golden/README.md and corpus.json say 172 (tools/golden/validate_corpus.py regenerates the same file), and #159's test_golden_corpus_dimension_10_write_is_conflict_and_no_tilt, which pins that count literally, now pins 172. Nothing else differs from the round-2 head 4b941a9: the diff against master is the same nine files plus that one-line test change.

Checks on a8573f2: ruff, mypy OWNd tests/typing, verify_library_standards.py, sync_documentation.py --check, validate_corpus.py (172 frames); pytest -p pytest_cov --cov=OWNd 3348 passed with verify_coverage.py at 100 % for all 28 OWNd modules; MyHOME v2-phase1-architecture f19adc57 suite with this branch on PYTHONPATH: 5601 passed, 1 deselected in 452.43s (the deselected one is the known test_check_all_documentation_real_tree pin signal). CI on a8573f2: all 8 checks green.


Generated by Claude Code

@GreenGrassBlueOcean GreenGrassBlueOcean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.The ASCII-only hardening for WHO 13 value fields is correct and minimal: fullwidth digits no longer decode as MAC/uptime/device-type, clock fields raise the same ValueError already used for other malformed values, and every pure-ASCII frame still decodes exactly as before. Documentation updates (conformance-matrix read form, Dimension 10/11/12 oracle caveats, #Z#C wording) are carefully qualified. The six new golden records come from already-committed plant captures. Branch is rebased onto current master, CI is green, and the MyHOME suite passes unchanged.Ready to merge.

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit 5cb9f2d into OpenWebNet-HA:master Nov 12, 2026
8 checks passed
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.

3 participants