Skip to content

docs(who-25): document gateway compatibility and hardware boundaries - #49

Merged
anotherjulien merged 3 commits into
mainfrom
docs/who25-gateway-compatibility
Oct 22, 2026
Merged

anotherjulien merged 3 commits into
mainfrom
docs/who25-gateway-compatibility

Conversation

@GreenGrassBlueOcean

@GreenGrassBlueOcean GreenGrassBlueOcean commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Document gateway compatibility for CEN+ and dry-contact/IR, keeping published support, recorded socket replies and recorded bus output separate.

  • Cite the two manufacturer compatibility tables independently and preserve their firmware distinctions.
  • Update the comparison to six pinned oracle v3 results. Keep the original v1 records clearly labelled as historical, since the PIC responder and settling changes affected the results.
  • Integrate the replacement MH200 firmware 2.1.0 capture from 22 October. Document the eight tested frames and their recorded NACK replies, without extending this to every WHO 25 command or incoming event handling.
  • Preserve the existing ScenarioPlus/contact documentation and all existing identifiers. Add 35 reviewed claims across this PR, including 15 for the new oracle and physical evidence.

Evidence and limits

The current oracle comparison is pinned to 8a61bbeb1d26354241debac84a7310142e5ab3a5 and covers two CEN+ inputs on six specified firmware versions. The physical capture covers a different MH200 firmware version and a setup with no WHO 25 device installed or configured. No independent physical bus recording was supplied. We inspected the published evidence; we did not run hardware or firmware ourselves.

The withdrawn 17 October capture supports no physical claims. The replacement is retained as an explicitly derived privacy projection: authentication, clock data and installation identifiers are removed, and the lighting control address is synthetic. Original fingerprints and record mappings remain available for verification.

Original oracle files, source fingerprints, provenance and review limits are recorded in the evidence review. The generated catalogue contains 74,401 claims and 8,008 retrieval chunks across 346 canonical documents.

Validation

  • Full python3 check.py passed: two clean deterministic builds, committed freshness, schemas, lifecycle and references, cross-artifact consistency, text hygiene, privacy gates and Device completeness.
  • Nine WHO 25 regression tests, 14 claim-framework tests and 55 evidence acceptance tests passed. All five evidence packages validate.
  • Mechanical ESG/ECV checks passed with zero objective failures. Existing advisory candidates remain review prompts.
  • Existing identifiers and source records were audited for preservation; Git LFS integrity passed.
  • Fresh private catalogue/source validation passed and its success status was published and verified for commit 5d6f5e9875194407631b087b85efb7fe13f2c955.
  • Hosted CI runs against the pushed revision separately. The complete unit suite was not rerun locally.

- Document gateway compatibility for WHO 25 (CEN+, Dry Contact / IR).

- Clarify supported gateways (MH200N, MH202, F454, MyHomeServer1) vs unsupported legacy MH200 (Model 4) returning gateway NACK (*#*0##).

- Cite normative Legrand WHO 25 v1.0.0 p. 13 and live bench verification.

- Update Machine KB section identities, chunk identities, claim coverage, and regenerated deterministic artifacts.
@anotherjulien

Copy link
Copy Markdown
Member

Thank you @GreenGrassBlueOcean! Documenting this gateway boundary is useful, and the published manuals do support the MH200 limitation 🙂

I’d like a few changes before merging, mainly to keep the conclusions aligned with their evidence and make sure the new information is represented properly in the Machine KB:

  1. Separate support from ACK/NACK behavior. The compatibility table currently associates supported gateways with ACK, but the oracle results show some tested CEN+ commands producing bus output while returning NACK, including on MyHomeServer1. Could you distinguish functional support, bus forwarding, and the socket reply, and qualify the observations by firmware and test setup?

  2. Correct the specification references. Page 13 of the dry-contact/IR specification explicitly marks MH200 as unsupported, but does not list MH202 or F454. It also documents F453 and firmware-dependent F453AV support. CEN+ has its own table on page 24 of the CEN/CEN+ specification. Please preserve those distinctions and cite the appropriate implementation evidence for the later gateways.

  3. Preserve and scope the MH200 bench evidence. Could you add or link the sanitized capture for the listed probes, including the valid *25*21#1*21## short press? The manual-backed lack of CEN+ and dry-contact/IR support can remain explicit, but “all WHO 25 frames return an immediate NACK” goes further than the demonstrated exchanges. Please distinguish published incompatibility from the responses observed on FW 2.1.0, and avoid describing *#25*21## as a published CEN+ status query.

  4. Add the new atomic claims. The three new sections contain factual compatibility assertions, but are currently classified as nonclaim. Please add reviewed claims with their evidence and applicability, then regenerate the coverage, artifacts, and test expectations.

  5. Update against current main and allocate unused IDs. The new retrieval IDs r001174–r001176 are already used by Device documentation published in Machine KB 0.2.0. Please preserve those existing assignments, allocate fresh retrieval IDs and the necessary new claim IDs, then regenerate and run the checks against the updated branch.

The checks passing is a good foundation. These changes would make the documented boundary traceable and usable by both readers and machine consumers. Thanks again!

Copy link
Copy Markdown
Member

I’ve prepared the documentation corrections and Machine KB integration on my side, keeping the manual-backed MH200 limitations and the scoped published oracle results 🙂

Could you share the sanitized MH200 FW 2.1.0 captures behind the bench observations? In particular, it would help to have:

  • The gateway model/firmware identification and the relevant bench setup, including whether a CEN+ device was connected and configured.
  • The exact transmitted and received frames for the probes you tested: *25*21#1*21##, *25*11#0*11##, and *#25*21##. Please keep the CEN+ short press, ScenarioPlus command, and status probe separate; the last is not a published CEN+ status query.
  • The session type, relative timings, and observation windows, including any preceding replies needed to establish which request the ACK or NACK belongs to.
  • If the conclusion also covers event forwarding, the corresponding event-session capture and the action that generated the event.

Please remove credentials, network identifiers, and unrelated installation details while preserving the protocol exchanges and timing needed to interpret them.

For now, I’ve left out the immediate-NACK, namespace-wide rejection, and event-handling claims that depend on those missing captures. Once the traces are available, we can add the observations with their firmware and test conditions stated explicitly. There’s no need for you to allocate the new KB IDs; I’m handling that with the integration changes.

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor Author

Thank you @anotherjulien for reconciling main, structuring the review package, and handling the Machine KB ID allocations!

Below are:

  1. The sanitized dual-session (*99*0## command + concurrent *99*1## event) wire captures from the physical BTicino MH200 (FW 2.1.0) covering gateway identification, positive-control replies, and the three separated WHO 25 probes (*25*21#1*21##, *25*11#0*11##, and *#25*21##), plus the remaining who25-messages stimuli.
  2. Ready-to-use manifest.json, frames.jsonl, and privacy.json artifacts matching the evidence/ schema.
  3. A summary of the firmware oracle work (OpenWebNet-HA/own-firmware-oracle) completed since revision af8ec0ceaff4c82a1a71f2a3ac5d01ad2095de7b (up to current main at 8a61bbeb1d26354241debac84a7310142e5ab3a5, oracle_version=3), which now includes emulated MH200 (020102, FW 2.1.2) and emulated F453 (010100, FW 1.1.0) across all 22 suites, as well as the $04 PIC responder ACK fix (fe0eff5) and CPU-idle settle window (settle_ms=3000) that resolved the earlier nack/silent harness artifacts on MH200N, F454, and MH202.

1. Physical MH200 (FW 2.1.0) Bench Setup & Gateway Identification

Hardware & Bus Setup

  • Gateway: Physical BTicino MH200 (1st-generation Scenario Programmer, article 003535, MAC OUI 00:03:50).
  • On-wire Gateway Identity:
    • *#13**15## $\rightarrow$ *#13**15*4## (MODEL = 4, MH200)
    • *#13**16## $\rightarrow$ *#13**16*2*1*0## (Firmware version 2.1.0)
    • *#13**23## $\rightarrow$ *#13**23*2*0*6## (Kernel version 2.0.6)
    • *#13**24## $\rightarrow$ *#13**24*3*0*5## (Distribution version 3.0.5)
    • *#1013*0*1## $\rightarrow$ *#1013**1*4## (WHO 1013 diagnostic object model 4)
  • Connected Bus Topology & WHO 25 Device Presence:
    • The gateway is connected to a live MyHOME SCS bus with WHO 1 lighting/dimmers (F414, F418U2), WHO 2 covers (including secondary bus 02 behind an F422 interface), WHO 5 burglar alarm, WHO 9 auxiliary channels, WHO 16 sound diffusion, and WHO 17 resident MH200 scenarios.
    • No physical CEN+ (WHO 25) pushbutton or WHO 25 dry-contact/IR interface (e.g., F428 in WHO 25 mode) is connected or configured on this installation, as the 1st-generation MH200 only supports Basic CEN (WHO 15) and WHO 17 scenes.
    • Scope of event-session observation: Because no physical WHO 25 transmitter is installed on the SCS bus to generate an inbound hardware bus event, the physical capture establishes:
      1. Outbound command-session rejection (*#*0## in 5.18–6.23 ms) for each tested probe.
      2. Complete silence on the concurrent *99*1## event session for all WHO 25 probes (whereas accepted queries and autonomous gateway broadcasts such as *#13**22*...## are actively delivered on the same event session).

2. Sanitized Dual-Session Wire Capture (Physical MH200 FW 2.1.0)

2.1 Primary Capture: Identification + Three Separated WHO 25 Probes (2,000 ms Observation Windows)

Two concurrent TCP connections were opened to port 20000:

  • event session (*99*1##): Authenticated at t = 17.89 ms and kept open continuously across the entire run.
  • command session (*99*0##): Authenticated at t = 34.94 ms.

Preceding identification queries (*#13**15##, *#13**16##, *#13**23##, *#13**24##, *#1013*0*1##) are spaced by ~400 ms and each completes with its data frame + *#*1## (ACK) on command (and data echo on event) before the next request is sent. Each of the three WHO 25 probes is then sent in its own isolated 2,000 ms observation window:

  t_rel (ms) | session | dir | frame                    | notes
-------------+---------+-----+--------------------------+------------------------------------------------------------------
     8.37 ms | event   | rx  | *#*1##                   | TCP greeting on event socket
     8.37 ms | event   | tx  | *99*1##                  | Request OpenWebNet event/monitor session
    14.31 ms | event   | rx  | *#<NONCE>##              | OPEN password challenge (nonce sanitized)
    14.33 ms | event   | tx  | *#<OPEN_PASSWORD_HASH>## | OPEN password response (credential sanitized)
    17.89 ms | event   | rx  | *#*1##                   | Event session established
    25.86 ms | command | rx  | *#*1##                   | TCP greeting on command socket
    25.86 ms | command | tx  | *99*0##                  | Request OpenWebNet command session
    31.48 ms | command | rx  | *#<NONCE>##              | OPEN password challenge (nonce sanitized)
    31.49 ms | command | tx  | *#<OPEN_PASSWORD_HASH>## | OPEN password response (credential sanitized)
    34.94 ms | command | rx  | *#*1##                   | Command session established
-------------+---------+-----+--------------------------+------------------------------------------------------------------
             |         |     |                          | --- Preceding Gateway Identification & Positive Controls ---
    34.96 ms | command | tx  | *#13**15##               | Query WHO 13 device model
    43.40 ms | command | rx  | *#13**15*4##             | Reply (+8.44 ms): MODEL = 4 (MH200)
    43.40 ms | command | rx  | *#*1##                   | Command ACK for *#13**15##
    70.03 ms | event   | rx  | *#13**15*4##             | Concurrent event session receives *#13**15*4## (+35.07 ms)
   442.23 ms | command | tx  | *#13**16##               | Query WHO 13 firmware version
   452.73 ms | command | rx  | *#13**16*2*1*0##         | Reply (+10.50 ms): Firmware 2.1.0
   452.73 ms | command | rx  | *#*1##                   | Command ACK for *#13**16##
   459.33 ms | event   | rx  | *#13**16*2*1*0##         | Concurrent event session receives *#13**16*2*1*0## (+17.10 ms)
   846.12 ms | command | tx  | *#13**23##               | Query WHO 13 kernel version
   855.54 ms | command | rx  | *#13**23*2*0*6##         | Reply (+9.42 ms): Kernel 2.0.6
   855.54 ms | command | rx  | *#*1##                   | Command ACK for *#13**23##
   863.10 ms | event   | rx  | *#13**23*2*0*6##         | Concurrent event session receives *#13**23*2*0*6## (+16.98 ms)
  1252.91 ms | command | tx  | *#13**24##               | Query WHO 13 distribution version
  1262.54 ms | command | rx  | *#13**24*3*0*5##         | Reply (+9.63 ms): Distribution 3.0.5
  1262.55 ms | command | rx  | *#*1##                   | Command ACK for *#13**24##
  1269.76 ms | event   | rx  | *#13**24*3*0*5##         | Concurrent event session receives *#13**24*3*0*5## (+16.85 ms)
  1654.99 ms | command | tx  | *#1013*0*1##             | Query WHO 1013 gateway diagnostic object model
  1664.69 ms | command | rx  | *#1013**1*4##            | Reply (+9.70 ms): Object Model = 4
  1664.70 ms | command | rx  | *#*1##                   | Command ACK for *#1013*0*1##
  1668.35 ms | event   | rx  | *#1013**1*4##            | Concurrent event session receives *#1013**1*4## (+13.36 ms)
-------------+---------+-----+--------------------------+------------------------------------------------------------------
             |         |     |                          | --- Probe 1: CEN+ Short Press (2,000 ms observation window) ---
  2062.42 ms | command | tx  | *25*21#1*21##            | CEN+ short press (pushbutton 1, virtual object 21)
  2068.13 ms | command | rx  | *#*0##                   | Immediate socket NACK (+5.71 ms); 0 event-session frames in 2.0 s
-------------+---------+-----+--------------------------+------------------------------------------------------------------
             |         |     |                          | --- Probe 2: ScenarioPlus Command (2,000 ms observation window) ---
  4068.83 ms | command | tx  | *25*11#0*11##            | ScenarioPlus activation command (scenario 0, object 11)
  4074.01 ms | command | rx  | *#*0##                   | Immediate socket NACK (+5.18 ms); 0 event-session frames in 2.0 s
-------------+---------+-----+--------------------------+------------------------------------------------------------------
             |         |     |                          | --- Probe 3: Dry-Contact Status Query (2,000 ms window) ---
  6064.32 ms | command | tx  | *#25*21##                | WHO 25 dry-contact/IR status query (not a CEN+ status query)
  6070.55 ms | command | rx  | *#*0##                   | Immediate socket NACK (+6.23 ms); 0 event-session frames in 2.0 s

2.2 Extended Capture: All who25-messages Stimuli + Positive Bus Control (*#9*0##) & Live Event-Session Clock Broadcast

In an extended capture run on the same physical MH200 (FW 2.1.0) including positive bus control *#9*0## and the remaining five stimuli from the oracle's who25-messages suite (*25*11#121*10##, *25*12*131##, *25*23#8*233##, *25*31#1*339##, *25*32#1*33##), note at t = 3655.29 ms that the event session actively delivered the gateway's autonomous WHO 13 clock broadcast (*#13**22*...##) while remaining silent for every WHO 25 frame:

  t_rel (ms) | session | dir | frame                                  | notes
-------------+---------+-----+----------------------------------------+----------------------------------------------------
  1580.55 ms | command | tx  | *#9*0##                                | Positive control: WHO 9 aux channel 0 status query
  1589.96 ms | command | rx  | *9*0*0##                               | Reply (+9.41 ms): Aux channel 0 OFF
  1589.96 ms | command | rx  | *#*1##                                 | Command ACK for *#9*0##
  1593.41 ms | event   | rx  | *9*0*0##                               | Event session receives *9*0*0## (+12.86 ms)
  1888.68 ms | command | tx  | *25*21#1*21##                          | CEN+ short press
  1894.21 ms | command | rx  | *#*0##                                 | Command NACK (+5.53 ms); event session silent
  2905.69 ms | command | tx  | *25*11#0*11##                          | ScenarioPlus ON
  2911.95 ms | command | rx  | *#*0##                                 | Command NACK (+6.26 ms); event session silent
  3655.29 ms | event   | rx  | *#13**22*13*53*04*002*06*17*10*2026##  | Autonomous WHO 13 clock broadcast on event session
  3919.55 ms | command | tx  | *#25*21##                              | Dry-contact/IR status query
  3924.43 ms | command | rx  | *#*0##                                 | Command NACK (+4.88 ms); event session silent
  4934.04 ms | command | tx  | *25*11#121*10##                        | ScenarioPlus command (*25*11#121*10##)
  4939.30 ms | command | rx  | *#*0##                                 | Command NACK (+5.26 ms); event session silent
  5547.15 ms | command | tx  | *25*12*131##                           | ScenarioPlus OFF command (*25*12*131##)
  5553.30 ms | command | rx  | *#*0##                                 | Command NACK (+6.15 ms); event session silent
  6152.60 ms | command | tx  | *25*23#8*233##                         | CEN+ extended press repeat (*25*23#8*233##)
  6199.72 ms | command | rx  | *#*0##                                 | Command NACK (+47.12 ms); event session silent
  6763.78 ms | command | tx  | *25*31#1*339##                         | Dry-contact/IR inactive (*25*31#1*339##)
  6790.81 ms | command | rx  | *#*0##                                 | Command NACK (+27.03 ms); event session silent
  7369.34 ms | command | tx  | *25*32#1*33##                          | Dry-contact/IR active (*25*32#1*33##)
  7395.69 ms | command | rx  | *#*0##                                 | Command NACK (+26.35 ms); event session silent

2.3 Sanitized evidence/cap-mh200-who25-boundary-20261017/ Files

frames.jsonl (command + event session exchanges)
{"seq": 1, "t_rel_ms": 0, "dir": "tx", "raw": "*#13**15##", "who": 13, "where": "", "what": null, "dimension": 15, "dimension_values": null, "annotation": "Command session: query WHO 13 gateway model", "interface": null, "scope": null}
{"seq": 2, "t_rel_ms": 8, "dir": "rx", "raw": "*#13**15*4##", "who": 13, "where": "", "what": null, "dimension": 15, "dimension_values": ["4"], "annotation": "Command session: gateway reports MODEL = 4 (MH200)", "interface": null, "scope": null}
{"seq": 3, "t_rel_ms": 8, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**15##", "interface": null, "scope": null}
{"seq": 4, "t_rel_ms": 35, "dir": "rx", "raw": "*#13**15*4##", "who": 13, "where": "", "what": null, "dimension": 15, "dimension_values": ["4"], "annotation": "Concurrent event session: receives *#13**15*4##", "interface": null, "scope": null}
{"seq": 5, "t_rel_ms": 407, "dir": "tx", "raw": "*#13**16##", "who": 13, "where": "", "what": null, "dimension": 16, "dimension_values": null, "annotation": "Command session: query WHO 13 firmware version", "interface": null, "scope": null}
{"seq": 6, "t_rel_ms": 418, "dir": "rx", "raw": "*#13**16*2*1*0##", "who": 13, "where": "", "what": null, "dimension": 16, "dimension_values": ["2", "1", "0"], "annotation": "Command session: gateway reports firmware 2.1.0", "interface": null, "scope": null}
{"seq": 7, "t_rel_ms": 418, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**16##", "interface": null, "scope": null}
{"seq": 8, "t_rel_ms": 424, "dir": "rx", "raw": "*#13**16*2*1*0##", "who": 13, "where": "", "what": null, "dimension": 16, "dimension_values": ["2", "1", "0"], "annotation": "Concurrent event session: receives *#13**16*2*1*0##", "interface": null, "scope": null}
{"seq": 9, "t_rel_ms": 811, "dir": "tx", "raw": "*#13**23##", "who": 13, "where": "", "what": null, "dimension": 23, "dimension_values": null, "annotation": "Command session: query WHO 13 kernel version", "interface": null, "scope": null}
{"seq": 10, "t_rel_ms": 821, "dir": "rx", "raw": "*#13**23*2*0*6##", "who": 13, "where": "", "what": null, "dimension": 23, "dimension_values": ["2", "0", "6"], "annotation": "Command session: gateway reports kernel 2.0.6", "interface": null, "scope": null}
{"seq": 11, "t_rel_ms": 821, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**23##", "interface": null, "scope": null}
{"seq": 12, "t_rel_ms": 828, "dir": "rx", "raw": "*#13**23*2*0*6##", "who": 13, "where": "", "what": null, "dimension": 23, "dimension_values": ["2", "0", "6"], "annotation": "Concurrent event session: receives *#13**23*2*0*6##", "interface": null, "scope": null}
{"seq": 13, "t_rel_ms": 1218, "dir": "tx", "raw": "*#13**24##", "who": 13, "where": "", "what": null, "dimension": 24, "dimension_values": null, "annotation": "Command session: query WHO 13 distribution version", "interface": null, "scope": null}
{"seq": 14, "t_rel_ms": 1228, "dir": "rx", "raw": "*#13**24*3*0*5##", "who": 13, "where": "", "what": null, "dimension": 24, "dimension_values": ["3", "0", "5"], "annotation": "Command session: gateway reports distribution 3.0.5", "interface": null, "scope": null}
{"seq": 15, "t_rel_ms": 1228, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**24##", "interface": null, "scope": null}
{"seq": 16, "t_rel_ms": 1235, "dir": "rx", "raw": "*#13**24*3*0*5##", "who": 13, "where": "", "what": null, "dimension": 24, "dimension_values": ["3", "0", "5"], "annotation": "Concurrent event session: receives *#13**24*3*0*5##", "interface": null, "scope": null}
{"seq": 17, "t_rel_ms": 1620, "dir": "tx", "raw": "*#1013*0*1##", "who": 1013, "where": "0", "what": null, "dimension": 1, "dimension_values": null, "annotation": "Command session: query WHO 1013 gateway diagnostic object model", "interface": null, "scope": null}
{"seq": 18, "t_rel_ms": 1630, "dir": "rx", "raw": "*#1013**1*4##", "who": 1013, "where": "", "what": null, "dimension": 1, "dimension_values": ["4"], "annotation": "Command session: gateway reports diagnostic object model 4", "interface": null, "scope": null}
{"seq": 19, "t_rel_ms": 1630, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#1013*0*1##", "interface": null, "scope": null}
{"seq": 20, "t_rel_ms": 1633, "dir": "rx", "raw": "*#1013**1*4##", "who": 1013, "where": "", "what": null, "dimension": 1, "dimension_values": ["4"], "annotation": "Concurrent event session: receives *#1013**1*4##", "interface": null, "scope": null}
{"seq": 21, "t_rel_ms": 2027, "dir": "tx", "raw": "*25*21#1*21##", "who": 25, "where": "21", "what": "21#1", "dimension": null, "dimension_values": null, "annotation": "Probe 1 (command session): CEN+ short press pushbutton 1 on virtual object 21 (2,000 ms observation window)", "interface": null, "scope": null}
{"seq": 22, "t_rel_ms": 2033, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Probe 1 reply (command session): immediate NACK (+5.71 ms); 0 frames on concurrent event session during 2,000 ms window", "interface": null, "scope": null}
{"seq": 23, "t_rel_ms": 4034, "dir": "tx", "raw": "*25*11#0*11##", "who": 25, "where": "11", "what": "11#0", "dimension": null, "dimension_values": null, "annotation": "Probe 2 (command session): ScenarioPlus command on WHERE 11 (2,000 ms observation window)", "interface": null, "scope": null}
{"seq": 24, "t_rel_ms": 4039, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Probe 2 reply (command session): immediate NACK (+5.18 ms); 0 frames on concurrent event session during 2,000 ms window", "interface": null, "scope": null}
{"seq": 25, "t_rel_ms": 6029, "dir": "tx", "raw": "*#25*21##", "who": 25, "where": "21", "what": null, "dimension": null, "dimension_values": null, "annotation": "Probe 3 (command session): WHO 25 dry-contact/IR status query on WHERE 21 (2,000 ms observation window)", "interface": null, "scope": null}
{"seq": 26, "t_rel_ms": 6036, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Probe 3 reply (command session): immediate NACK (+6.23 ms); 0 frames on concurrent event session during 2,000 ms window", "interface": null, "scope": null}

3. Firmware Oracle Work (OpenWebNet-HA/own-firmware-oracle) Since af8ec0c

Since af8ec0ceaff4c82a1a71f2a3ac5d01ad2095de7b (oracle_version=1), we completed a large body of work in OpenWebNet-HA/own-firmware-oracle (up to current main at 8a61bbeb1d26354241debac84a7310142e5ab3a5, oracle_version=3) that directly explains the MH200 behavior and refines the cross-gateway WHO 25 results:

3.1 MH200 (020102, FW 2.1.2) Unpacked, Catalogued & Emulated Across All 22 Suites (PR #45, PR #47)

  • Image provenance: Official Legrand service pack MH200_020102.zip (SHA-256: 373305c11b2d72ff08912c83d1b4770d2315983eab90bdd3a9b1c04350b2a96b) $\rightarrow$ MH200_020102.fwz (SHA-256: 53848de84438f9a395501cf955d20762951faacb8449e1271ed5e027448e1e3b).
  • Daemon & stack_open.xml architecture (oracle/targets/MH200/020102.yaml, findings/MH200/README.md):
    • home/bticino/cfg/stack_open.xml (SHA-256: f78076d517b665340269706ffcd7baa30be20eb64b709a72a116076688e07f07) configures openserver (SHA-256: 539f943d7f298be10a77d5990b7163c0fe1a8616c45d865486291d71b3899583) and scsserver (SHA-256: ff49feebdaaad64b4cdcc61b625b3fba002637086c7b0de7397968627da98106) alongside bt_luci (SHA-256: f4a2d6945f94476e26e0a653e173da7fb9ddd5709b211dd44ec9471001052fb7, port 40001, WHO 0, 1, 2, 14, 15, 1001), bt_device (WHO 13, 1013), bt_termo (WHO 4), bt_energia (WHO 3), bt_difson (WHO 16, 1016), bt_alarm (WHO 5, 9), bt_vct (WHO 6, 7), and bt_taskmanager (WHO 17).
    • WHO 25 is not registered in stack_open.xml on MH200 020102, and no translator daemon binds WHO 25. When openserver receives any WHO 25 frame on TCP 20000, it finds no registered translator for WHO=25 and immediately returns *#*0## (nack, silent, -) without forwarding anything to scsserver or /dev/ttyPIC. That directly explains the 5–6 ms TCP-layer NACK observed on physical MH200 FW 2.1.0.
  • Emulated MH200 020102 WHO 25 suite results (at 8a61bbeb1d26354241debac84a7310142e5ab3a5):

3.2 F453 (010100, FW 1.1.0, WS_6DIN) Unpacked, Catalogued & Emulated Across All 22 Suites (PR #51)

  • Extracted WS_6DIN_010100.fwz (SHA-256: da02a317e44827beef0b51fd65e4fdf4f53f36865eec85dbca130c14685ed626) from MyHOME_Suite_030538.exe (findings/F453/README.md).
  • In F453 010100, home/bticino/cfg/stack_open.xml (SHA-256: 8774ea4c780dec01ad9e1f9139d2eaae3c899149a6a99ca6da8562b620b682b6) explicitly routes WHO 0.1.2.14.15.25.1000 to bt_luci (SHA-256: 1f8d6e18490b1266a506109ab3084f31cdbc7fe35966bd11a9c4d6d804e42b9d).
  • In results/F453/010100/oracle/full/who25-messages.tsv and trace-corpus.tsv, F453 (010100) returns ack + out (bus:24 30 34 42 ... | own:*25*...##) for *25*12*131##, *25*21#1*21##, *25*23#8*233##, *25*31#1*339##, *25*32#1*33##, and *25*11#0*11##, corroborating the published manual tables (WHO_15-25.pdf p. 24 and WHO_25.pdf p. 13) with direct firmware execution.

3.3 Why MH200N, F454, and MH202 Verdicts Changed Between af8ec0c (oracle_version=1) and 8a61bbe (oracle_version=3)

At revision af8ec0ceaff4c82a1a71f2a3ac5d01ad2095de7b (oracle_version=1, settle_ms=300):

  1. PicResponder did not yet acknowledge $04 outbound bus frames on /dev/ttyPIC (fixed in fe0eff5: "fix(oracle): acknowledge $04 bus frames, lengthen command timeout and reconnect on timeout"). Because scsserver/bt_luci on MH200N (010108) and F454 (020051) wait for the PIC's $04 ACK before returning *#*1## to openserver, they timed out and returned nack despite emitting the bus frame (out).
  2. Step collection used a fixed settle_ms=300 cutoff instead of waiting for emulated daemon CPU-idle with settle_ms=3000 (fixed in 2e45bfa and 1b2e26d), which cut off MH202 (010024) before its bus output flushed (ack / silent).

Under oracle_version=3 at 8a61bbeb1d26354241debac84a7310142e5ab3a5, across all 12 emulated gateways:

Gateway Firmware *25*21#1*21## & *25*23#8*233## (who25-messages.tsv) *25*11#0*11## (trace-corpus.tsv L79) *#25*21## (trace-corpus.tsv L49) who25-messages.tsv Git Blob / SHA-256 at 8a61bbe
MH200 020102 nack / silent (-) nack / silent (-) nack / silent (-) 169ebf04e8934fb29b93ff585f96ef7271150f34
8895d1c545f80ae4c0177a6cc2ea135d7112b11983cc8875119f38a21100ba5e
MH200N 010108 ack / out (bus:24 30 34 42... | own:*25*...##) ack / out (bus:24 30 34 42 31 30 31 39 33 30 30 0d | own:*25*11#0*11##) nack / silent (-) 22568253f2f90aeb352f421ff3f52a55b88dafee
1dfd7a802bb2a763276187bc13649d7071deefb492f9a9f7a070957feef30148
F453 010100 ack / out (bus:24 30 34 42... | own:*25*...##) ack / out (bus:24 30 34 42 31 30 31 39 33 30 30 0d | own:*25*11#0*11##) nack / silent (-) d4a3f0b4c77762f9301d5c04db34ef90bb481b8e
ac9d494099c530c5a8500d4cb74db6400add3ffe9f6862dadea0fdaccb01a6df
F453AV 030014 ack / out (bus:24 30 34 42... | own:*25*...##) ack / out (bus:24 30 34 42 31 30 31 39 33 30 30 0d | own:*25*11#0*11##) nack / silent (-) 42ad2fb4d3171889d0ccd1cd17ec02b5f9ab84ce
5989266aa693b33cb119c2e3e1fade2a46f121efbd4c1a397fdea67d4ded91bf
F454 020051 ack / out (bus:24 30 34 42... | own:*25*...##) ack / out (bus:24 30 34 42 31 30 31 39 33 30 30 0d | own:*25*11#0*11##) nack / silent (-) dd9eebd6b5bb4792ef3f561fbebf44d1a7904d55
f6669f98ab5297be2ae36566138d360c1d0e5f2aa3e71fad493f5f248bd70ba1
F459 020105 ack / out (bus:24 30 34 42... | own:*25*...##) ack / out (bus:24 30 34 42 31 30 31 39 33 30 30 0d | own:*25*11#0*11##) nack / silent (-) 703d33abdf103145d68be398ea668c65de5f7dec
917c7e85308757c757dd89631914d938e07ccbd31dae6756765ad48c4e3c0dcf
MH202 010024 ack / out (bus:24 30 34 42... | own:*25*...##) ack / out (own:*#*0##) nack / silent (-) c1e437f88708d28b9840720a759d829cd58f2020
53e76991e2324d02f101a1b66da692a450f5d0ba3274de76642debe0ab462fab
MyHomeServer1 028206 nack / out (bus:24 30 34 42...) nack / out (bus:24 30 34 42 31 30 31 39 33 30 30 0d) nack / silent (-) febe9c37b4358d61474d916b3e62c9f059dfe818
a444bf040cf2af9bd887a66251de4c90730e1d61f6940e767e2cea5e0643522f
F460 020012 nack / out (bus:24 30 30 34 42...) nack / out (bus:24 30 30 34 42 31 30 31 39 33 30 30 0d) nack / silent (-) 0c307cde8731cc441ed2e3b32f0c5df5248068ee
c247ba5d7c43abdf903d3b871b0742a6730d5742e17ec65266124fda6720f2fb
F461 020011 nack / out (bus:24 30 30 34 42...) nack / out (bus:24 30 30 34 42 31 30 31 39 33 30 30 0d) nack / silent (-) 329fb1e52a8cfb58c19124d9ecc126e5dc20cbea
d98089d6d8c8157e69569bf16767ebdb14584e6028d65b8b5668e16aa890943a
F450 020010 nack / silent (-) nack / silent (-) nack / silent (-) eee8dfb608ae2987d85f3426686bef9bfc7adf92
b796a99f205ef8d9d5ec8444430044c15752d836916fedb67a30c74ef53b7da6
H4684 020054 nack / silent (-) nack / silent (-) nack / silent (-) 6e62367fa0f2fc668f0aa0d7f85045434528801c
1922b20d87b9336b09839a36a95c0441e7355dc1329938386875ef65e8153c47

Let me know if you need any additional probes run on the physical MH200 (FW 2.1.0)!

@anotherjulien

Copy link
Copy Markdown
Member

Thanks for providing the captures and explaining the newer oracle results 🙂

This gives us enough to document the rejection of the tested commands on MH200 firmware 2.1.0. We’ll keep that conclusion specific to those tests and leave incoming WHO 25 event handling open.

Could you also attach the manifest.json and privacy.json you mentioned? I can see frames.jsonl in the comment, but not those two files.

I can handle the small formatting corrections and update our documentation and Machine KB to reflect the newer oracle results. I don’t think we need further hardware tests for this PR’s current scope.

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor Author

Thanks @anotherjulien, and sorry, a correction before you build on my last comment.

I can't attach manifest.json / privacy.json for the 2026-10-17 capture. The raw session log behind those two tables was not retained, so I can't produce a package that is traceable to an actual recording. Please treat the capture details in that comment (the 5–6 ms NACK latencies, the 2,000 ms windows and the concurrent event-session observations) as unsubstantiated for now, and don't derive claims from them. The frames.jsonl I posted also doesn't validate: what is a string ("21#1", "11#0") at seq 21 and 23, but evidence-frame.schema.json requires an integer or null. It also records which session a frame came from only in the annotation text.

I'll redo the physical MH200 (FW 2.1.0) capture and post a complete package: manifest.json, privacy.json and a schema-valid frames.jsonl. It will cover gateway identification, positive controls, and the three probes you asked for, kept separate (*25*21#1*21##, *25*11#0*11##, *#25*21##), plus the rest of the who25-messages inputs. Every probe gets its own observation window on concurrent *99*0## / *99*1## sessions. All values will be computed from the recording itself. privacy.json will carry the SHA-256 of the raw session log, which I'll keep and can share. The bus is a live installation with no WHO 25 device installed or configured, so incoming WHO 25 event handling stays open, as you proposed.

Two corrections to the oracle part of that comment. Everything else checks out against 8a61bbeb1d26354241debac84a7310142e5ab3a5: all twelve who25-messages.tsv blob SHAs, the SHA-256s and the result rows match, and fe0eff5 / 2e45bfa / 1b2e26d are ancestors of it.

  • F453 010100 stack_open.xml is 8774ea4c780dec01ad9e1f9139d2eaae3c899149a6a99ca6da8562b620b782b6 (findings/F453/README.md, results/F453/010100/manifest.tsv). I posted …b620b682b6, which is wrong.
  • On MH200 020102, bt_luci serves WHO 1, 2, 14, 15 and 1001 (findings/MH200/README.md:20), not WHO 0. Also, "WHO 25 is not registered in stack_open.xml" is an inference: the repository commits that file's hash, not its contents. It rests on the documented translator list and the nack/silent results, not on a published copy of the file.

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor Author

As promised, here is the new physical capture from the BTicino MH200 (FW 2.1.0, WHO 13 MODEL 4), recorded 2026-10-22T08:59:19Z. It replaces the 2026-10-17 capture details I withdrew above.

Setup. Concurrent event (*99*1##) and command (*99*0##) sessions on TCP 20000. The gateway sits on the main SCS bus of a live installation, with no WHO 25 device installed or configured. The order was: five identification queries, two positive controls (*#9*0##, *#13**22##), a 10 s manual window, then the eight WHO 25 frames. Each WHO 25 frame was sent alone, with a 2,000 ms observation window on both sessions. During the manual window I pressed a physical lighting pushbutton, and the event session delivered *1*0*57## / *1*1*57##. So the event session was demonstrably live for real bus events, not only for command-session echoes.

Result. Every WHO 25 frame got a command-session NACK (*#*0##), and no WHO 25 frame appeared on the event session in any window:

Frame Command reply Event-session WHO 25 frames (2,000 ms)
*25*21#1*21## NACK +57.43 ms 0
*25*11#0*11## NACK +25.39 ms 0
*#25*21## NACK +19.58 ms 0
*25*11#121*10## NACK +23.40 ms 0
*25*12*131## NACK +34.94 ms 0 (1 autonomous *#13**22*…## clock broadcast)
*25*23#8*233## NACK +24.20 ms 0
*25*31#1*339## NACK +39.49 ms 0
*25*32#1*33## NACK +14.69 ms 0

The identification queries and both positive controls were each answered with data plus ACK, and echoed on the event session. Please note the latencies (about 15–57 ms). They differ from the 5–6 ms in my withdrawn comment, so please use these. Incoming WHO 25 event handling remains untested, because there is no WHO 25 transmitter on this bus.

Provenance. The capture was recorded with a small stdlib-only script, and every value in the package is computed from the recording. Before posting I checked that:

  • the package passes evidence/validate_evidence.py;
  • the OPEN nonce and password response are redacted in every file;
  • the gateway address is not recorded anywhere.

privacy.json carries the SHA-256 of raw-session-log.jsonl, which is the full two-session timeline including the handshake. The raw log is included below, so the hash can be checked directly. Suggested package directory: evidence/cap-mh200-who25-boundary-20261022/.

manifest.json (4350 bytes, SHA-256 a92418b22ae45543da4b88c67c21464153e8d2312d95dca7becfd26fc393c9dd)
{
  "evidence_id": "EVID-MH200-WHO25-BOUNDARY",
  "capture_id": "cap-mh200-who25-boundary-20261022",
  "title": "BTicino MH200 (FW 2.1.0) command-session replies to tested WHO 25 frames",
  "capture_kind": "controlled_experiment",
  "epistemic_status": "observed",
  "confidence": "high",
  "evidence_class": "public_capture",
  "recorded_at": "2026-10-22T08:59:19Z",
  "installation_id": "58964012-179b-5120-bfb6-b511ee2bcd0f",
  "bus_id": "44420761-807c-5d98-a745-f3c794d37f30",
  "gateway_id": "c9c79529-1399-5c4f-ac30-ec3bc466b291",
  "environment": {
    "gateway_model": "MH200",
    "gateway_firmware": "2.1.0",
    "bus_topology": "Main SCS bus of a live installation; no WHO 25 device is installed or configured",
    "who13_device_type": 4
  },
  "device_under_test": {
    "sku": "BTicino MH200 gateway",
    "description": "Gateway under test (WHO 13 MODEL 4)",
    "configured_where": "0",
    "pseudonym_device_id": "MH200-GW-01"
  },
  "summary": "Physical BTicino MH200 (FW 2.1.0, WHO 13 MODEL 4) probed over concurrent event (*99*1##) and command (*99*0##) sessions. Command-session replies: *25*21#1*21## -> NACK after 57.43 ms; *25*11#0*11## -> NACK after 25.39 ms; *#25*21## -> NACK after 19.58 ms; *25*11#121*10## -> NACK after 23.40 ms; *25*12*131## -> NACK after 34.94 ms; *25*23#8*233## -> NACK after 24.20 ms; *25*31#1*339## -> NACK after 39.49 ms; *25*32#1*33## -> NACK after 14.69 ms. Event-session WHO 25 frames per probe window: 0, 0, 0, 0, 0, 0, 0, 0. Main SCS bus of a live installation; no WHO 25 device is installed or configured. Incoming WHO 25 event handling is not tested.",
  "test_recipe": [
    "1. Open an event session (*99*1##) and then a command session (*99*0##) on TCP 20000; OPEN password authentication values are redacted.",
    "2. Send *#13**15## (Query WHO 13 gateway model); wait for ACK/NACK. Observed: ACK after 10.77 ms.",
    "3. Send *#13**16## (Query WHO 13 firmware version); wait for ACK/NACK. Observed: ACK after 55.24 ms.",
    "4. Send *#13**23## (Query WHO 13 kernel version); wait for ACK/NACK. Observed: ACK after 9.95 ms.",
    "5. Send *#13**24## (Query WHO 13 distribution version); wait for ACK/NACK. Observed: ACK after 41.00 ms.",
    "6. Send *#1013*0*1## (Query WHO 1013 gateway diagnostic object model); wait for ACK/NACK. Observed: ACK after 49.19 ms.",
    "7. Send *#9*0## (Positive control: WHO 9 auxiliary channel 0 status request); wait for ACK/NACK. Observed: ACK after 34.51 ms.",
    "8. Send *#13**22## (Positive control: WHO 13 date/time request); wait for ACK/NACK. Observed: ACK after 111.22 ms.",
    "9. Press a physical lighting pushbutton during a 10.0 s window to confirm the event session delivers bus events.",
    "10. Send *25*21#1*21## (CEN+ short press, pushbutton 1, WHERE 21) on the command session and observe both sessions for 2000 ms. Observed: NACK after 57.43 ms.",
    "11. Send *25*11#0*11## (ScenarioPlus command, WHAT 11#0, WHERE 11) on the command session and observe both sessions for 2000 ms. Observed: NACK after 25.39 ms.",
    "12. Send *#25*21## (WHO 25 status request on WHERE 21 (not a published CEN+ status query)) on the command session and observe both sessions for 2000 ms. Observed: NACK after 19.58 ms.",
    "13. Send *25*11#121*10## (ScenarioPlus command, WHAT 11#121, WHERE 10) on the command session and observe both sessions for 2000 ms. Observed: NACK after 23.40 ms.",
    "14. Send *25*12*131## (ScenarioPlus command, WHAT 12, WHERE 131) on the command session and observe both sessions for 2000 ms. Observed: NACK after 34.94 ms.",
    "15. Send *25*23#8*233## (CEN+ extended press, pushbutton 8, WHERE 233) on the command session and observe both sessions for 2000 ms. Observed: NACK after 24.20 ms.",
    "16. Send *25*31#1*339## (Dry-contact/IR WHAT 31#1, WHERE 339) on the command session and observe both sessions for 2000 ms. Observed: NACK after 39.49 ms.",
    "17. Send *25*32#1*33## (Dry-contact/IR WHAT 32#1, WHERE 33) on the command session and observe both sessions for 2000 ms. Observed: NACK after 14.69 ms."
  ],
  "claims_supported": [
    "claim.who25.gateway_boundaries.mh200_unsupported"
  ],
  "related_documents": [
    "functional/who-25-transversal/README.md",
    "functional/who-25-transversal/cen-plus.md",
    "functional/who-25-transversal/dry-contact-ir.md"
  ],
  "frame_count": 47
}
privacy.json (834 bytes, SHA-256 269d41e2a2c350484a4bca2fab91b0fdce7635cdecc904ea74eaf8364616b748)
{
  "classification": "sanitized",
  "removed_value_classes": [
    "credential",
    "network_address"
  ],
  "notes": "Captured by capture_who25_mh200.py from a physical gateway on a live installation. The gateway network address is not recorded and the OPEN password nonce/response are redacted in every output. 0 event-session frames of other WHOs (installation traffic) are omitted from frames.jsonl; per-window counts are kept in the annotations. Full raw session log (auth values redacted) retained by the contributor: raw-session-log.jsonl, SHA-256 2300d334629ad0dc0f61377e0e91e1b22e11359eaf1279c3fa39dba44faacb28.",
  "pseudonymization": {
    "device_ids": {
      "0": "MH200-GW-01"
    }
  },
  "retained_local_addresses": [
    "0",
    "10",
    "11",
    "131",
    "21",
    "233",
    "33",
    "339",
    "57"
  ]
}
frames.jsonl (12618 bytes, SHA-256 2890a121975a334eebe29047cbe4dba137128cf40cb6c34d4134b2eb4bfceadf)
{"seq": 1, "t_rel_ms": 0, "dir": "tx", "raw": "*#13**15##", "who": 13, "where": "", "what": null, "dimension": 15, "dimension_values": null, "annotation": "Command session: Query WHO 13 gateway model", "interface": null, "scope": null}
{"seq": 2, "t_rel_ms": 11, "dir": "rx", "raw": "*#13**15*4##", "who": 13, "where": "", "what": null, "dimension": 15, "dimension_values": ["4"], "annotation": "Command session: reply to *#13**15##", "interface": null, "scope": null}
{"seq": 3, "t_rel_ms": 11, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**15## (+10.77 ms)", "interface": null, "scope": null}
{"seq": 4, "t_rel_ms": 17, "dir": "rx", "raw": "*#13**15*4##", "who": 13, "where": "", "what": null, "dimension": 15, "dimension_values": ["4"], "annotation": "Event session: received during the *#13**15## step", "interface": null, "scope": null}
{"seq": 5, "t_rel_ms": 431, "dir": "tx", "raw": "*#13**16##", "who": 13, "where": "", "what": null, "dimension": 16, "dimension_values": null, "annotation": "Command session: Query WHO 13 firmware version", "interface": null, "scope": null}
{"seq": 6, "t_rel_ms": 486, "dir": "rx", "raw": "*#13**16*2*1*0##", "who": 13, "where": "", "what": null, "dimension": 16, "dimension_values": ["2", "1", "0"], "annotation": "Command session: reply to *#13**16##", "interface": null, "scope": null}
{"seq": 7, "t_rel_ms": 486, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**16## (+55.24 ms)", "interface": null, "scope": null}
{"seq": 8, "t_rel_ms": 493, "dir": "rx", "raw": "*#13**16*2*1*0##", "who": 13, "where": "", "what": null, "dimension": 16, "dimension_values": ["2", "1", "0"], "annotation": "Event session: received during the *#13**16## step", "interface": null, "scope": null}
{"seq": 9, "t_rel_ms": 884, "dir": "tx", "raw": "*#13**23##", "who": 13, "where": "", "what": null, "dimension": 23, "dimension_values": null, "annotation": "Command session: Query WHO 13 kernel version", "interface": null, "scope": null}
{"seq": 10, "t_rel_ms": 894, "dir": "rx", "raw": "*#13**23*2*0*6##", "who": 13, "where": "", "what": null, "dimension": 23, "dimension_values": ["2", "0", "6"], "annotation": "Command session: reply to *#13**23##", "interface": null, "scope": null}
{"seq": 11, "t_rel_ms": 894, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**23## (+9.95 ms)", "interface": null, "scope": null}
{"seq": 12, "t_rel_ms": 901, "dir": "rx", "raw": "*#13**23*2*0*6##", "who": 13, "where": "", "what": null, "dimension": 23, "dimension_values": ["2", "0", "6"], "annotation": "Event session: received during the *#13**23## step", "interface": null, "scope": null}
{"seq": 13, "t_rel_ms": 1291, "dir": "tx", "raw": "*#13**24##", "who": 13, "where": "", "what": null, "dimension": 24, "dimension_values": null, "annotation": "Command session: Query WHO 13 distribution version", "interface": null, "scope": null}
{"seq": 14, "t_rel_ms": 1332, "dir": "rx", "raw": "*#13**24*3*0*5##", "who": 13, "where": "", "what": null, "dimension": 24, "dimension_values": ["3", "0", "5"], "annotation": "Command session: reply to *#13**24##", "interface": null, "scope": null}
{"seq": 15, "t_rel_ms": 1332, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**24## (+41.00 ms)", "interface": null, "scope": null}
{"seq": 16, "t_rel_ms": 1332, "dir": "rx", "raw": "*#13**24*3*0*5##", "who": 13, "where": "", "what": null, "dimension": 24, "dimension_values": ["3", "0", "5"], "annotation": "Event session: received during the *#13**24## step", "interface": null, "scope": null}
{"seq": 17, "t_rel_ms": 1745, "dir": "tx", "raw": "*#1013*0*1##", "who": 1013, "where": "0", "what": null, "dimension": 1, "dimension_values": null, "annotation": "Command session: Query WHO 1013 gateway diagnostic object model", "interface": null, "scope": null}
{"seq": 18, "t_rel_ms": 1794, "dir": "rx", "raw": "*#1013**1*4##", "who": 1013, "where": "", "what": null, "dimension": 1, "dimension_values": ["4"], "annotation": "Command session: reply to *#1013*0*1##", "interface": null, "scope": null}
{"seq": 19, "t_rel_ms": 1794, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#1013*0*1## (+49.19 ms)", "interface": null, "scope": null}
{"seq": 20, "t_rel_ms": 1794, "dir": "rx", "raw": "*#1013**1*4##", "who": 1013, "where": "", "what": null, "dimension": 1, "dimension_values": ["4"], "annotation": "Event session: received during the *#1013*0*1## step", "interface": null, "scope": null}
{"seq": 21, "t_rel_ms": 2200, "dir": "tx", "raw": "*#9*0##", "who": 9, "where": "0", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: Positive control: WHO 9 auxiliary channel 0 status request", "interface": null, "scope": null}
{"seq": 22, "t_rel_ms": 2235, "dir": "rx", "raw": "*9*0*0##", "who": 9, "where": "0", "what": 0, "dimension": null, "dimension_values": null, "annotation": "Command session: reply to *#9*0##", "interface": null, "scope": null}
{"seq": 23, "t_rel_ms": 2235, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#9*0## (+34.51 ms)", "interface": null, "scope": null}
{"seq": 24, "t_rel_ms": 2237, "dir": "rx", "raw": "*9*0*0##", "who": 9, "where": "0", "what": 0, "dimension": null, "dimension_values": null, "annotation": "Event session: received during the *#9*0## step", "interface": null, "scope": null}
{"seq": 25, "t_rel_ms": 2641, "dir": "tx", "raw": "*#13**22##", "who": 13, "where": "", "what": null, "dimension": 22, "dimension_values": null, "annotation": "Command session: Positive control: WHO 13 date/time request", "interface": null, "scope": null}
{"seq": 26, "t_rel_ms": 2752, "dir": "rx", "raw": "*#13**22*10*58*49*002*04*22*10*2026##", "who": 13, "where": "", "what": null, "dimension": 22, "dimension_values": ["10", "58", "49", "002", "04", "22", "10", "2026"], "annotation": "Command session: reply to *#13**22##", "interface": null, "scope": null}
{"seq": 27, "t_rel_ms": 2752, "dir": "rx", "raw": "*#*1##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ACK for *#13**22## (+111.22 ms)", "interface": null, "scope": null}
{"seq": 28, "t_rel_ms": 2752, "dir": "rx", "raw": "*#13**22*10*58*49*002*04*22*10*2026##", "who": 13, "where": "", "what": null, "dimension": 22, "dimension_values": ["10", "58", "49", "002", "04", "22", "10", "2026"], "annotation": "Event session: received during the *#13**22## step", "interface": null, "scope": null}
{"seq": 29, "t_rel_ms": 10976, "dir": "rx", "raw": "*1*0*57##", "who": 1, "where": "57", "what": 0, "dimension": null, "dimension_values": null, "annotation": "Event session: frame during the manual pushbutton window", "interface": null, "scope": null}
{"seq": 30, "t_rel_ms": 12441, "dir": "rx", "raw": "*1*1*57##", "who": 1, "where": "57", "what": 1, "dimension": null, "dimension_values": null, "annotation": "Event session: frame during the manual pushbutton window", "interface": null, "scope": null}
{"seq": 31, "t_rel_ms": 13165, "dir": "tx", "raw": "*25*21#1*21##", "who": 25, "where": "21", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: CEN+ short press, pushbutton 1, WHERE 21", "interface": null, "scope": null}
{"seq": 32, "t_rel_ms": 13223, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*21#1*21## (+57.43 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 33, "t_rel_ms": 15176, "dir": "tx", "raw": "*25*11#0*11##", "who": 25, "where": "11", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ScenarioPlus command, WHAT 11#0, WHERE 11", "interface": null, "scope": null}
{"seq": 34, "t_rel_ms": 15202, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*11#0*11## (+25.39 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 35, "t_rel_ms": 17181, "dir": "tx", "raw": "*#25*21##", "who": 25, "where": "21", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: WHO 25 status request on WHERE 21 (not a published CEN+ status query)", "interface": null, "scope": null}
{"seq": 36, "t_rel_ms": 17201, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *#25*21## (+19.58 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 37, "t_rel_ms": 19189, "dir": "tx", "raw": "*25*11#121*10##", "who": 25, "where": "10", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: ScenarioPlus command, WHAT 11#121, WHERE 10", "interface": null, "scope": null}
{"seq": 38, "t_rel_ms": 19213, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*11#121*10## (+23.40 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 39, "t_rel_ms": 21195, "dir": "tx", "raw": "*25*12*131##", "who": 25, "where": "131", "what": 12, "dimension": null, "dimension_values": null, "annotation": "Command session: ScenarioPlus command, WHAT 12, WHERE 131", "interface": null, "scope": null}
{"seq": 40, "t_rel_ms": 21230, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*12*131## (+34.94 ms); event session carried 0 WHO 25 and 1 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 41, "t_rel_ms": 21896, "dir": "rx", "raw": "*#13**22*10*59*08*002*04*22*10*2026##", "who": 13, "where": "", "what": null, "dimension": 22, "dimension_values": ["10", "59", "08", "002", "04", "22", "10", "2026"], "annotation": "Event session: received during the *25*12*131## step", "interface": null, "scope": null}
{"seq": 42, "t_rel_ms": 23195, "dir": "tx", "raw": "*25*23#8*233##", "who": 25, "where": "233", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: CEN+ extended press, pushbutton 8, WHERE 233", "interface": null, "scope": null}
{"seq": 43, "t_rel_ms": 23219, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*23#8*233## (+24.20 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 44, "t_rel_ms": 25203, "dir": "tx", "raw": "*25*31#1*339##", "who": 25, "where": "339", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: Dry-contact/IR WHAT 31#1, WHERE 339", "interface": null, "scope": null}
{"seq": 45, "t_rel_ms": 25242, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*31#1*339## (+39.49 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
{"seq": 46, "t_rel_ms": 27209, "dir": "tx", "raw": "*25*32#1*33##", "who": 25, "where": "33", "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: Dry-contact/IR WHAT 32#1, WHERE 33", "interface": null, "scope": null}
{"seq": 47, "t_rel_ms": 27223, "dir": "rx", "raw": "*#*0##", "who": null, "where": null, "what": null, "dimension": null, "dimension_values": null, "annotation": "Command session: NACK for *25*32#1*33## (+14.69 ms); event session carried 0 WHO 25 and 0 other frames in the 2000 ms window", "interface": null, "scope": null}
raw-session-log.jsonl (5298 bytes, SHA-256 2300d334629ad0dc0f61377e0e91e1b22e11359eaf1279c3fa39dba44faacb28)
{"t_ms": 90.8, "session": "event", "dir": "rx", "raw": "*#*1##", "phase": "handshake"}
{"t_ms": 90.881, "session": "event", "dir": "tx", "raw": "*99*1##", "phase": "handshake"}
{"t_ms": 149.128, "session": "event", "dir": "rx", "raw": "*#<NONCE>##", "phase": "handshake"}
{"t_ms": 149.458, "session": "event", "dir": "tx", "raw": "*#<OPEN_PASSWORD_HASH>##", "phase": "handshake"}
{"t_ms": 154.488, "session": "event", "dir": "rx", "raw": "*#*1##", "phase": "handshake"}
{"t_ms": 168.551, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "handshake"}
{"t_ms": 168.623, "session": "command", "dir": "tx", "raw": "*99*0##", "phase": "handshake"}
{"t_ms": 228.262, "session": "command", "dir": "rx", "raw": "*#<NONCE>##", "phase": "handshake"}
{"t_ms": 228.331, "session": "command", "dir": "tx", "raw": "*#<OPEN_PASSWORD_HASH>##", "phase": "handshake"}
{"t_ms": 233.739, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "handshake"}
{"t_ms": 233.829, "session": "command", "dir": "tx", "raw": "*#13**15##", "phase": "run"}
{"t_ms": 244.576, "session": "command", "dir": "rx", "raw": "*#13**15*4##", "phase": "run"}
{"t_ms": 244.603, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 250.679, "session": "event", "dir": "rx", "raw": "*#13**15*4##", "phase": "run"}
{"t_ms": 664.901, "session": "command", "dir": "tx", "raw": "*#13**16##", "phase": "run"}
{"t_ms": 720.12, "session": "command", "dir": "rx", "raw": "*#13**16*2*1*0##", "phase": "run"}
{"t_ms": 720.139, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 726.94, "session": "event", "dir": "rx", "raw": "*#13**16*2*1*0##", "phase": "run"}
{"t_ms": 1118.157, "session": "command", "dir": "tx", "raw": "*#13**23##", "phase": "run"}
{"t_ms": 1128.082, "session": "command", "dir": "rx", "raw": "*#13**23*2*0*6##", "phase": "run"}
{"t_ms": 1128.109, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 1135.239, "session": "event", "dir": "rx", "raw": "*#13**23*2*0*6##", "phase": "run"}
{"t_ms": 1525.018, "session": "command", "dir": "tx", "raw": "*#13**24##", "phase": "run"}
{"t_ms": 1566.002, "session": "command", "dir": "rx", "raw": "*#13**24*3*0*5##", "phase": "run"}
{"t_ms": 1566.017, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 1566.167, "session": "event", "dir": "rx", "raw": "*#13**24*3*0*5##", "phase": "run"}
{"t_ms": 1978.442, "session": "command", "dir": "tx", "raw": "*#1013*0*1##", "phase": "run"}
{"t_ms": 2027.62, "session": "command", "dir": "rx", "raw": "*#1013**1*4##", "phase": "run"}
{"t_ms": 2027.633, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 2027.67, "session": "event", "dir": "rx", "raw": "*#1013**1*4##", "phase": "run"}
{"t_ms": 2433.97, "session": "command", "dir": "tx", "raw": "*#9*0##", "phase": "run"}
{"t_ms": 2468.47, "session": "command", "dir": "rx", "raw": "*9*0*0##", "phase": "run"}
{"t_ms": 2468.479, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 2470.448, "session": "event", "dir": "rx", "raw": "*9*0*0##", "phase": "run"}
{"t_ms": 2874.805, "session": "command", "dir": "tx", "raw": "*#13**22##", "phase": "run"}
{"t_ms": 2986.013, "session": "command", "dir": "rx", "raw": "*#13**22*10*58*49*002*04*22*10*2026##", "phase": "run"}
{"t_ms": 2986.026, "session": "command", "dir": "rx", "raw": "*#*1##", "phase": "run"}
{"t_ms": 2986.082, "session": "event", "dir": "rx", "raw": "*#13**22*10*58*49*002*04*22*10*2026##", "phase": "run"}
{"t_ms": 11210.326, "session": "event", "dir": "rx", "raw": "*1*0*57##", "phase": "run"}
{"t_ms": 12674.474, "session": "event", "dir": "rx", "raw": "*1*1*57##", "phase": "run"}
{"t_ms": 13399.233, "session": "command", "dir": "tx", "raw": "*25*21#1*21##", "phase": "run"}
{"t_ms": 13456.663, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 15410.198, "session": "command", "dir": "tx", "raw": "*25*11#0*11##", "phase": "run"}
{"t_ms": 15435.59, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 17414.758, "session": "command", "dir": "tx", "raw": "*#25*21##", "phase": "run"}
{"t_ms": 17434.334, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 19423.095, "session": "command", "dir": "tx", "raw": "*25*11#121*10##", "phase": "run"}
{"t_ms": 19446.493, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 21428.717, "session": "command", "dir": "tx", "raw": "*25*12*131##", "phase": "run"}
{"t_ms": 21463.659, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 22130.169, "session": "event", "dir": "rx", "raw": "*#13**22*10*59*08*002*04*22*10*2026##", "phase": "run"}
{"t_ms": 23429.111, "session": "command", "dir": "tx", "raw": "*25*23#8*233##", "phase": "run"}
{"t_ms": 23453.311, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 25436.762, "session": "command", "dir": "tx", "raw": "*25*31#1*339##", "phase": "run"}
{"t_ms": 25476.248, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}
{"t_ms": 27442.438, "session": "command", "dir": "tx", "raw": "*25*32#1*33##", "phase": "run"}
{"t_ms": 27457.124, "session": "command", "dir": "rx", "raw": "*#*0##", "phase": "run"}

Copy link
Copy Markdown
Member

Thanks @GreenGrassBlueOcean for the fresh capture and for working through the oracle corrections with us 🙂

I’ve integrated the updated evidence into the documentation and Machine KB, and all CI checks have passed. This is ready to merge from my side. Thanks for taking the time to get it right!

@anotherjulien
anotherjulien merged commit 7102cff into main Oct 22, 2026
7 checks passed
@anotherjulien
anotherjulien deleted the docs/who25-gateway-compatibility branch October 22, 2026 14:51
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