Skip to content

feat(gateway): WHO 13 dimension 27 model-name decoder - #139

Draft
gdluck wants to merge 1 commit into
OpenWebNet-HA:masterfrom
gdluck:feat/who13-model-name-and-who1001-diagnostic
Draft

gdluck wants to merge 1 commit into
OpenWebNet-HA:masterfrom
gdluck:feat/who13-model-name-and-who1001-diagnostic

Conversation

@gdluck

@gdluck gdluck commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Requested by A

Summary

The physical F461 2.0.11 answers *#13**27## with *#13**27*70#52#54#49##: its model name as #-separated ASCII codes (70 52 54 49 = "F461"). A physical MyHomeServer1 3.87.13 answers *#13**27*77#121#72#111#109#101#83#101#114#118#101#114#49## ("MyHomeServer1"). The dimension-reply regex accepts only digits in values, so OWNMessage.parse returned None, the event session handed the raw str on and MyHOME logged "Data received is not a message". It was the only one of the 69 events captured on 2026-10-23 (68 typed, 1 raw) that did not come back as a typed class.

Refs OpenWebNet-HA/MyHOME#799 (both captures). The WHO 1001 diagnostic event that this PR carried at first is now its own PR (see Companion): different evidence, different reviewers.

Changes

  • OWNd/message/base.py: the WHO 13 Dimension 27 shape joins _HASH_VALUE_REPLIES (introduced by fix(message): parse '#'-prefixed dimension 14 setpoint values #138), bound to ASCII digits with the # grammar explicit: ^\*#(?P<who>13)\*(?P<where>)(?P<where_param>)\*(?P<dimension>27)(?P<dimension_param>)(?P<dimension_value>\*[0-9]{1,3}(?:#[0-9]{1,3})+)##$, re.ASCII. (?:#[0-9]{1,3})+ because a single code (*#13**27*70##) is indistinguishable from a plain numeric reply, which _DIMENSION_REQUEST_REPLY already parses, and every known BTicino / Legrand model string has more than one character; three digits per code at most because printable ASCII is 32..126 (a second-opinion review found that an unbounded [0-9]+ would let a frame with a 4300-digit code reach int() and raise). Non-ASCII digits are no frame: OWNMessage.parse('*#13**27*٧٠#٥٢##') is None (the .isascii() workaround of the previous revision is gone with its cause).
  • OWNd/message/gateway.py: OWNGatewayEvent.model_name decodes Dimension 27 only when the reply has exactly one value that contains # and every code names a printable ASCII character (32..126); human_readable_log reads "Gateway's model name is: F461.". The general *-separated form (*#13**27*70*52*54*49##) and a single code without # (*#13**27*70##) are not a model name: model_name stays None (fail closed). *#13**27## is still an OWNGatewayCommand dimension request.
  • CHANGELOG.md: entry under [Unreleased] / Added.

State Diagram

stateDiagram-v2
    direction TB

    state "OWNMessage.parse(*#13**27*...##)" as Parse {
        [*] --> General
        General --> Plain : *#13**27*70*52*54*49## , *-separated, general regex\nOWNGatewayEvent, model_name None , fail closed
        General --> HashAllowlist : # inside the value - general regex does not match
        HashAllowlist --> Name : *#13**27*70#52#54#49## , one value, #-separated, printable ASCII\nOWNGatewayEvent, model_name F461 , new - before - None, Data received is not a message
        HashAllowlist --> NoName : *#13**27*70#7#300## , non-printable code\nOWNGatewayEvent, model_name None
        HashAllowlist --> NoneResult : *#13**27*70#52#54#49*1##, *#13**12*70#52##, *#1*11*1*#5##\nNone , unchanged
    }
Loading

Where are the traces

Subsystem Capture Plant Frames verified by this PR
WHO 13 dimension 27 trace PR: OpenWebNet-HA/MyHOME#799 (merged), f461_live_2026-10-23 run 1 (run1_oracle_replay_readonly); the frame is in pending_decoder_rx of tests/test_trace_oracle_validation.py there, citing this PR BTicino F461 2.0.11 (*#13**16*2*0*11##) *#13**27## -> *#13**27*70#52#54#49## ("F461"), returned on the command session and delivered on the event session
WHO 13 dimension 27 trace PR: OpenWebNet-HA/MyHOME#799 (merged), myhomeserver1_live_2026-10-24 run 1 (run1_identify) MyHomeServer1 3.87.13 (*#13**16*3*87*13##) *#13**27## -> *#13**27*77#121#72#111#109#101#83#101#114#118#101#114#49## ("MyHomeServer1")

What does the Oracle say

No oracle stimulus exists for this frame: no suite sends *#13**27##. The two physical captures above are the evidence.

Testing

  • tests/test_who13_model_name.py (new, 19 cases): the F461 and MyHomeServer1 replies parse as OWNGatewayEvent with the raw dimension_value, dimension_param == [], the decoded model_name, the log line and str(message) unchanged; OWNEvent.parse (the event session's entry point) gives the same object as OWNMessage.parse on four shapes (review point 2); non-printable codes, the *-separated form and a single code without # leave model_name None; *#13**27## is a dimension request; a second value after the codes, mixed separators, a code of four digits, a code of 5000 digits, another dimension with a # value and a WHO 1 # value do not parse at all; the Unicode-digit frame is None from OWNMessage.parse and the event session's usual invalid event from OWNEvent.parse, with no name.
  • Fail before / pass after: on master 5 fail, 5 pass (OWNMessage.parse('*#13**27*70#52#54#49##') has no model_name; the request shape and the negative cases already pass); on this branch all 10 pass. Re-verified 2026-10-24 on both plants with plain OWNd master as the sender: *#13**27## -> *#13**27*70#52#54#49## (F461 2.0.11, 14 ms) and *#13**27*77#121#72#111#109#101#83#101#114#118#101#114#49## (MyHomeServer1 3.87.13, 21 ms); this head decodes them to F461 / MyHomeServer1, master to None.
  • Full suite (head 3ffe8a8 on master f3efc1d, after fix(message): parse '#'-prefixed dimension 14 setpoint values #138): 2357 passed; mypy clean, ruff clean.

Live retest (2026-10-24)

Confirmed, narrowly: on the F461 2.0.11 with this decoder, *#13**27## -> *#13**27*70#52#54#49## and the HA log reads Gateway's model name is: F461. instead of "Data received is not a message". It is not wired into MyHOME's diagnostics (identification.who13_model stays null); that is a separate MyHOME change.

Companion

MyHOME: none required now. OpenWebNet-HA/MyHOME#802 (the warning rate limit) was closed; the two pending_decoder_rx entries in MyHOME's tests/test_trace_oracle_validation.py are dropped at the pin bump (see the CI note). OpenWebNet-HA/MyHOME#799 carries the traces. Related OWNd PR (split from this one): #145, merged.

Revision 3ffe8a8 (review 5684988925, after #138 landed). Rebuilt as one commit on master f3efc1d: no merge commit (6a2d3ba gone), authorship reset (68c9992 / e2fdfd1 folded in), _HASH_VALUE_REPLIES bound to ASCII with the + quantifier (point 1), OWNEvent.parse symmetry pinned (point 2), Companion updated (point 3). On top of the review: codes capped at three digits.

CI note (expected red check)

The MyHOME test suite job runs MyHOME against this branch. MyHOME's tests/test_trace_oracle_validation.py lists the two dimension-27 frames of the merged trace PR OpenWebNet-HA/MyHOME#799 in pending_decoder_rx and, by the maintainers' request on that PR, asserts loudly that they still parse to None until the OWNd pin is bumped. This decoder makes them parse, so that single job is red by construction (remove … from pending_decoder_rx: the OWNd pin now decodes it). The cleanup is a one-line MyHOME change that belongs with the pin bump; every other check is green.

🤖 Generated with Claude Code

@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!

@gdluck
gdluck marked this pull request as draft October 24, 2026 13:39
@gdluck
gdluck force-pushed the feat/who13-model-name-and-who1001-diagnostic branch from 0bf88b6 to d51cf77 Compare October 24, 2026 15:49
@gdluck gdluck changed the title feat(message): WHO 13 dimension 27 model-name decoder and WHO 1001 dimension 11 diagnostic event feat(gateway): WHO 13 dimension 27 model-name decoder Oct 24, 2026
@gdluck

gdluck commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Self-correction in 68c9992 + e2fdfd1 (no review asked for it). @GreenGrassBlueOcean a quick confirmation would be welcome.

Context. OWNGatewayEvent.model_name decodes *#13**27*C1#C2#…## as ASCII codes.

How it was found. Reading the decoder for edge inputs it does not test: c.isdigit() and int(c) both accept Unicode decimal digits, so *#13**27*٧٠#٥٢## decoded to "F4" although the isdigit() guard was already redundant after the base regex. No gateway sends that, so it is a parser hole, not a wire bug.

Live check (2026-10-26 16:45 UTC). *#13**27## answers 70#52#54#49 ("F461") on the F461 2.0.11 and 77#121#72#111#109#101#83#101#114#118#101#114#49 ("MyHomeServer1") on a MyHomeServer1 3.87.13: ASCII decimal codes joined by #, as the PR describes.

What changed. The codes must be ASCII digits (c.isascii() and c.isdigit()); a test pins the Unicode frame to an OWNGatewayEvent with model_name None. 11 cases; suite 1918 passed on the branch base; ruff / mypy clean. One note for sequencing: this PR and #138 edit the same _HASH_VALUE_REPLIES hunk in base.py; whichever merges second needs a one-tuple rebase, I will do it as soon as the first lands.

@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.

The live F461 2.0.11 and MyHomeServer1 3.87.13 captures in OpenWebNet-HA/MyHOME#799 establish the wire encoding (*#13**27*C1#C2#...##), and failing closed on *-separated or non-printable values is clean. Note that neither OWNd nor MyHOME sends *#13**27## or reads OWNGatewayEvent.model_name in production today (MyHOME identifies gateways via WHO 13 dim 15 + WHO 1013 dim 1 + SSDP/UPnP), so this is passive event-session decoding when an external client queries *#13**27## (plus clearing the two pending_decoder_rx entries in MyHOME on the next pin bump).

Please hold rebasing until #138 lands so _HASH_VALUE_REPLIES in OWNd/message/base.py only has to be merged once, then address these points:

  1. Bind _HASH_VALUE_REPLIES to ASCII digits and + quantifier in OWNd/message/base.py:

    re.compile(
        r"^\*#(?P<who>13)\*(?P<where>)(?P<where_param>)\*(?P<dimension>27)(?P<dimension_param>)(?P<dimension_value>\*[0-9]+(?:#[0-9]+)+)##$",
        re.ASCII,
    )
    • In the current pattern (\*\d+(?:#\d+)*), the zero-# branch of * is unreachable for ASCII digits because _DIMENSION_REQUEST_REPLY matches *#13**27*70## first; using (?:#[0-9]+)+ makes the #-separated grammar explicit (and note in the comment that all known BTicino/Legrand model strings are multi-character, as a single code *#13**27*70## is indistinguishable from a plain numeric dimension reply).
    • Because \d+ lacked re.ASCII, *#13**27*٧٠#٥٢## matched _HASH_VALUE_REPLIES and parsed as OWNGatewayEvent(model_name=None) (requiring the .isascii() workaround in gateway.py and test_model_name_with_unicode_digit_codes_is_not_decoded). Non-ASCII digits are not legal OpenWebNet wire frames and should return None from OWNMessage.parse rather than constructing an OWNGatewayEvent.
  2. Pin OWNEvent.parse(...) symmetry alongside OWNMessage.parse(...) in tests/test_who13_model_name.py (as in #62).

  3. Housekeeping:

    • Update the Companion section (OpenWebNet-HA/MyHOME#802 was closed).
    • Rebase cleanly onto master without merge commit 6a2d3ba and reset commit authorship on 68c9992 / e2fdfd1 (Mac Book <macbook@Macs-MacBook-Pro.local>).

@gdluck

gdluck commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Why the MyHOME conformance / MyHOME test suite job is red here, and why it stays red until the pin bump.

The job runs MyHOME's suite with this checkout in place of the pinned OWNd==2.0.0b11. One test fails (5020 passed): the trace-replay check reports remove *#13**27*70#52#54#49## from pending_decoder_rx: the OWNd pin now decodes it (and the same for the MyHomeServer1 model-name frame). MyHOME lists the WHO 13 dimension 27 model-name frames in pending_decoder_rx, the set of trace frames the pinned OWNd cannot decode yet; this PR adds the decoder, so against this checkout the assertion fires on purpose, telling MyHOME to drop those frames from the list when its pin moves to a release that carries this decoder. The MyHOME-side removal is a pin-bump change, not something this PR can do.

The CONFLICTING state is separate: this branch waits for #138 to land (its _HASH_VALUE_REPLIES pattern is the base this decoder's regex builds on), as agreed in review 5684988925; it is rebased onto master right after.

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor

@gdluck please rebase than i will merge

*OpenWebNet-HA#13**27*C1#C2#...## is the gateway model name as '#'-separated ASCII
codes: 70#52#54#49 is "F461" on a live F461 2.0.11 and
77#121#...OpenWebNet-HA#49 is "MyHomeServer1" on a live MyHomeServer1 3.87.13
(MyHOME#799 traces). OWNGatewayEvent.model_name decodes that single
'#'-separated value; the '*'-separated form and a single code stay
undecoded. The frame pattern joins _HASH_VALUE_REPLIES bound to ASCII
digits with an explicit (?:#[0-9]+)+ grammar, so non-ASCII digit codes
are no frame at all. OWNEvent.parse and OWNMessage.parse agree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gdluck
gdluck force-pushed the feat/who13-model-name-and-who1001-diagnostic branch from b46cb1f to 3ffe8a8 Compare October 29, 2026 07:34
@gdluck

gdluck commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Rebased: one commit 3ffe8a8 on master f3efc1d (after #138), no merge commit, authorship reset. Review 5684988925 points: (1) _HASH_VALUE_REPLIES bound to re.ASCII with (?:#[0-9]{1,3})+ (three digits per code, printable ASCII is 32..126; an unbounded [0-9]+ would let a thousands-of-digits code reach int()), the .isascii() workaround removed, *#13**27*٧٠#٥٢## -> None; (2) OWNEvent.parse == OWNMessage.parse pinned on four shapes; (3) Companion updated (MyHOME#802 closed). 2357 passed, ruff and mypy clean; the MyHOME test suite job stays red by construction (pending_decoder_rx, pin bump). Ready to merge from my side.

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.

3 participants