Skip to content

feat(lighting): WHO 1001 dimension 11 diagnostic event - #145

Merged
GreenGrassBlueOcean merged 4 commits into
OpenWebNet-HA:masterfrom
gdluck:feat/who1001-diagnostic-event
Oct 27, 2026
Merged

GreenGrassBlueOcean merged 4 commits into
OpenWebNet-HA:masterfrom
gdluck:feat/who1001-diagnostic-event

Conversation

@gdluck

@gdluck gdluck commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Requested by A

Summary

*#1001*74*11*111110111111111111110111## is the actuator autodiagnostic frame the oracle gateways push together with *1*19*74## when a status request (*#1*74##) reaches the emulated faulty actuator (and the physical MH200 capture behind that finding); the same dimension is also answered on request (*#1001*WHERE*11##) by the lights read on the live F461 and MyHomeServer1 (see Hardware Verification). OWNd returned a generic OWNEvent with who == 1001, so nothing exposed the mask. The bit meanings are marked Open in the oracle, so this PR exposes the mask as sent and interprets nothing. Split out of #139.

Changes

  • OWNd/message/lighting.py: OWNLightingDiagnosticEvent (an OWNEvent subclass, registered for WHO 1001, exported from OWNd.message) exposes diagnostic_mask as sent and cleared_bits (1-based from the left; (6, 21) for the oracle mask, () when every bit is set). Only the single-value bit-string shape is decoded: *#1001*74*11*101*5## (a trailing value) fails closed with diagnostic_mask is None (the first version of this PR would have reported "101"). Other WHO 1001 shapes (*#1001*74##, dimension 12) have diagnostic_mask is None. MyHOME's device_health.py reads who / dimension / dimension_value on the event and is unaffected.
  • scripts/sync_documentation.py + README.md: the WHO catalog row for Lighting now reads **1** / **1001** with the diagnostic class listed (python scripts/sync_documentation.py --update).
  • CHANGELOG.md: entry under [Unreleased] / Added.

State Diagram

stateDiagram-v2
    direction TB

    state "1. Gateway on *#1*74## to a faulty actuator (oracle what19; physical MH200 capture)" as Bus {
        [*] --> Request
        Request --> Fault : *1*19*74## then *#1001*74*11*<24-bit mask>##\nF453, F453AV, F459, F460, F461, MH200N, MyHomeServer1
        Request --> MaskOnly : *#1001*74*11*<mask>## only , MH202
        Request --> What19Only : *1*19*74## only , F454, H4684, MH200
        Request --> Refused : nack / silent , F450
    }

    state "2. OWNMessage.parse of the WHO 1001 frame" as Parse {
        [*] --> Who1001
        Who1001 --> Diagnostic : *#1001*74*11*<24 bits>## or *7*<24 bits>## , one value, exactly 24 bits 0/1\nOWNLightingDiagnosticEvent, diagnostic_mask as sent, cleared_bits , 6, 21\n, before - generic OWNEvent, nothing exposed
        Who1001 --> NoMask : *#1001*74*11*101*5##, *#1001*74*12*5##\nOWNLightingDiagnosticEvent, diagnostic_mask None , fail closed
    }
Loading

Where are the traces

Subsystem Capture Plant Frames verified by this PR
WHO 1001 dimension 11 after WHAT 19 own-firmware-oracle findings/MH200N/what19.md, physical capture EVID-MH200-WHAT19-FAULT (agree with the emulated output; bits 6 and 21 left Open) physical MH200 *1*19*WHERE## + *#1001*WHERE*11*<mask>##
WHO 1001 on a plant without a faulty actuator trace PR: OpenWebNet-HA/MyHOME#799 (merged), f461_live_2026-10-23 run 1 BTicino F461 2.0.11 *#1001*74## NACK (no WHO 1001 device on this plant)
WHO 1001 dimension 11 read on request (2026-10-24, plain OWNd master as sender) hardware verification note only (per the maintainer: no new trace PR needed; the committed MyHOME fixtures below cover the push paths) BTicino F461 2.0.11, healthy BT-F411/4 lights 06 / 07 *#1001*06*11## -> *#1001*06*11*111111111111111101101111## (ACK 120 ms), same for 07; group *#1001*0*11## NACK after 3.0 s but *#1001*05*11*…##, 06, 07, 09 and *#1013*257*11*111111111111111111111111## arrive on the event session
same hardware verification note only MyHomeServer1 3.87.13, healthy lights *#1001*0014*11## and *#1001*0010*11## -> same mask 111111111111111101101111 (ACK 139 / 157 ms); group *#1001*0*11## ACK after 3.7 s with *#1001*0*11*111111111111111101101111## on the command session, about 80 per-device masks and *#1013*256*11*…## on the event session

What does the Oracle say

Suite what19, results/<P>/<V>/oracle/full/what19.tsv, input *#1*74##:

Gateway Reply / verdict Output
F453 010100, F453AV 030014, F459 020105, F460 020012, F461 020011, MH200N 010108, MyHomeServer1 028206 ack / out *1*19*74## then *#1001*74*11*111110111111111111110111##
MH202 010024 ack / out *#1001*74*11*111110111111111111110111## only
F454 020051, H4684 020054, MH200 020102 ack / out *1*19*74## only
F450 020010 nack / silent none

findings/MH200N/what19.md states that the gateway "only translates the bitmask verbatim" and leaves the meaning of bits 6 and 21 Open; this PR exposes the mask and interprets nothing. (The findings page's fleet list omits F453 and MH202; the TSV rows above include them.)

Revision 6014d03 (review of 2026-10-26 20:32). The mask is accepted on DIMENSION 7 (BITMASK_DIA_A) as well as 11; WHO 1001 joins the WHOs whose WHERE carries #4#II in OWNMessage.interface (so interface, unique_id 1001-74#4#01 and the log line work behind an F422); the log subject handles an empty WHERE; cleared_bits is stored once; the docstring, README row and CHANGELOG say WHO 1001 covers lighting and automation actuators alike and list the four ways the mask is seen (after WHAT 19, alongside sweep replies on MH200N, on a direct read, as DIMENSION 7 on WHERE 0); tests/test_firmware_oracle_conformance.py asserts the class, mask and cleared bits; origin/master 050dc1d is merged into the branch (CHANGELOG conflict resolved, the WHO 1001 entry under "Added").

Testing

  • tests/test_who1001_diagnostic_event.py (new, 13 cases): the oracle mask parses as OWNLightingDiagnosticEvent / OWNEvent with where == "74", dimension == 11, the mask kept, cleared_bits == (6, 21) and the log line; an all-ones mask gives (); eight other shapes assert the exact parsed class and no mask (trailing value, dimension 12, 1-, 23- and 26-bit strings, 24 characters with a non-bit, the bare dimension 11 request and *#1001*74## as plain OWNCommand); two malformed frames (# inside the value, non-digits) do not parse at all; *#1001*74## is asserted not to be a diagnostic event. (Revision c42dc2f: the mask must be exactly 24 bits, as every capture is and the docstring said; the first revision took any non-empty 0/1 string, and the negative-shape test returned before asserting on three of its frames.)
  • Fail before / pass after: on master the module fails at collection (OWNLightingDiagnosticEvent does not exist; with that import removed the WHO 1001 frame is a plain OWNEvent); on this branch all 20 pass (6014d03: WHERE 0, 0014, routed 74#4#01, empty and the status-shaped *1001*4*0##, DIMENSION 7 shapes, is_general pinned).
  • Full suite (head c42dc2f on the branch base f04d8aa): 1920 passed, 1 skipped; 100 % statement coverage, mypy clean, ruff clean, scripts/sync_documentation.py --check (README regenerated in this PR) and scripts/verify_library_standards.py pass.

Live retest (2026-10-24)

Exercised live on 2026-10-24 (table above): dimension 11 is also a readable per-device dimension: the two lights read on each plant answered on request, and the group request made every light that answered (05 / 06 / 07 / 09 on the F461, about 80 WHEREs on the MyHomeServer1) report the same mask 111111111111111101101111 (bits 17 and 20 cleared, counted from the left); this head parses every one of those captured frames as OWNLightingDiagnosticEvent with cleared_bits == (17, 20), while *#1013*25x*11*…## stays OWNGatewayDiagnosticEvent. Inference, unverified: that mask is the healthy pattern of these firmware generations and the oracle's 111110111111111111110111 (bits 6 and 21) the fault pattern; the bit meanings stay open and the decoder interprets nothing.The push paths are covered by committed MyHOME fixtures: issue_619 (F453AV 3.0.14, five unloaded F414 dimmers pushing *1*19*WHERE## + the (6, 21) mask), issue_466 / issue_733 (MH200, WHERE 74), issue_434 / issue_746 (MH200N 1.1.8, the mask 170-210 ms after normal *1*0/1*WHERE## sweep replies, (4, 17, 20, 23), no WHAT 19) and issue_466 again for DIMENSION 7 on WHERE 0 from a MyHomeServer1 cover diagnostic. The MyHomeServer1 group reply with where == "0" is worth a test case; I will add it if you want it in this PR.

Companion

None. Split from #139 (WHO 13 dimension 27 model name).

🤖 Generated with Claude Code

`*#1001*WHERE*11*<mask>##` is the actuator autodiagnostic frame a
gateway pushes with `*1*19*WHERE##` when a status request reaches a
faulty actuator: own-firmware-oracle what19 (input `*michnovka#1*74##`) shows it
on F453, F453AV, F459, F460, F461, MH200N, MyHomeServer1 (after
`*1*19*74##`) and MH202 (alone), and a physical MH200 capture agrees
(findings/MH200N/what19.md, EVID-MH200-WHAT19-FAULT). OWNd returned a
generic OWNEvent with who == 1001 and exposed nothing.

OWNLightingDiagnosticEvent (registered for WHO 1001, exported) exposes
diagnostic_mask as sent and cleared_bits (1-based from the left). The
bit meanings are Open in the oracle, so nothing is interpreted. Only the
single-value bit-string shape is decoded; `*#1001*74*11*101*5##` fails
closed. README WHO catalog lists WHO 1001 (sync_documentation --update).

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!

… class

`OWNLightingDiagnosticEvent` took any non-empty 0/1 string as the
autodiagnostic mask although every capture (oracle `what19` on eight
gateways, physical MH200) carries exactly 24 bits and the docstring said
so: `*#1001*74*11*1##` reported "mask is 1 (cleared bits: none)". The mask
now requires a single 24-character 0/1 value; other DIMENSION 11 value
frames keep `diagnostic_mask` None. The log line computes the cleared
bits directly (the `or ()` fallback could never trigger).

Tests: the negative-shape test returned before asserting on three of its
five frames and skipped the mask assertions for the request shape; it now
asserts the exact parsed class per frame (eight frames incl. 1, 23 and 26
bit strings and a 24-character non-bit string), the two malformed frames
are pinned as not parsing at all, and the status-request test asserts it
is an `OWNCommand`, not a diagnostic event.

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

gdluck commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Self-correction in c42dc2f (no review asked for it). @GreenGrassBlueOcean could you confirm the reading, or say if you see it differently?

Context. OWNLightingDiagnosticEvent exposes the WHO 1001 DIMENSION 11 autodiagnostic mask as sent; the docstring says the gateway copies the actuator's 24-bit mask verbatim.

How it was found. Reading the decoder against the base-class contract instead of its own tests: the mask check was value and set(value) <= {"0", "1"}, so *#1001*74*11*1## reported "mask is 1 (cleared bits: none)" although no capture has anything but 24 bits; self.cleared_bits or () could never be None at that point (the mask is assigned one line earlier); and the negative-shape test returned early for frames OWNMessage.parse turns into None and skipped the mask assertions when the parse gave a plain OWNCommand, so three of its five frames asserted nothing, and the "status request is not a diagnostic event" test only checked who == 1001.

Live check (F461 2.0.11, 2026-10-26 16:45 UTC). A dimension 11 read on a healthy light, *#1001*06*11##, is ACKed in 0.13 s with *#1001*06*11*111111111111111101101111##: exactly 24 bits (bits 17 and 20 cleared), the same shape as the fault captures.

What changed. The mask requires a single 24-character 0/1 value (other DIMENSION 11 value frames keep diagnostic_mask None), the log computes the cleared bits directly, and the tests assert the exact parsed class per frame (13 cases, two malformed frames pinned to None, the status request pinned to OWNCommand). Suite 1920 passed, ruff / mypy / docs check clean.

@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 c42dc2f — confirming your reading on the self-correction: requiring a single 24-character 0/1 value is the right call (s2o_selfDiagnosticsA in libdiag.so.0.0 unpacks 3 SCS hex bytes = 24 bits, OPEN.db types the diagnostic bitmask as 24 bits, and every oracle row and plant capture we have is 24 bits), and fixing test_other_who_1001_shapes_have_no_mask so the OWNCommand and malformed cases actually run assertions was needed.

Tested c42dc2f merged locally with current master (050dc1d): after resolving the CHANGELOG.md conflict, 2118 passed (100% statement coverage on OWNd/message/lighting.py), ruff, scripts/sync_documentation.py --check, and scripts/verify_library_standards.py are clean, and MyHOME's tests/test_device_health.py + tests/test_trace_oracle_validation.py pass (104 passed).

Before approving:

  1. Rebase onto master (050dc1d): CHANGELOG.md conflicts with #136 / #140 / #148, which also prevented GitHub from building refs/pull/145/merge (0 CI check-runs ran on c42dc2f; the 8 green checks are from 4ae2264).

  2. DIMENSION 7 (BITMASK_DIA_A) vs DIMENSION 11: OWNLightingDiagnosticEvent currently gates diagnostic_mask on self._dimension == 11. In OPEN.db (OpenWebNet-Encyclopedia diagnostics/dimension-reference.md), DIMENSION 7 (*#[WHO]*[WHERE]*7*[BITMASK_DIA_A]##) is the 24-bit diagnostic bitmask A alongside DIMENSION 11, MyHOME's device_health.py already pairs AUTODIAG_DIMENSIONS = (7, 11), and our committed MyHomeServer1 traces (MyHOME tests/fixtures/traces/issue_466/myhome_trace_MyHomeServer1_monostable_cover_diagnostic_2026-09-27T13-51-19.json and ..._scenarios_cover_diagnostic_...) carry *#1001*0*7*111111111111111101101111## — with the exact same 111111111111111101101111 ((17, 20)) mask as your live DIMENSION 11 reads. Right now that frame parses as OWNLightingDiagnosticEvent with diagnostic_mask is None. Either accept (7, 11) here (updating the docstring / README.md / CHANGELOG.md accordingly) or document explicitly why OWNLightingDiagnosticEvent only decodes 11 and leaves 7 as None.

  3. Routed (#4#NN), empty-WHERE, and where == "0" shapes (OWNd/message/base.py, OWNd/message/lighting.py):

    • OWNMessage.interface (base.py:297-305) checks self._who in [0, 1, 2, 14, 15]. For an actuator behind an F422 interface (*#1001*74#4#01*11*111110111111111111110111##, OPEN.db [I3][I4]; MyHOME's message_where() works around this via _where_param), message.interface is None, message.unique_id drops #4#01 ("1001-74" instead of "1001-74#4#01"), and _human_readable_log omits {self._interface_log_text} (unlike OWNLightingEvent).
    • On the empty-WHERE service form *#1001**11*<24-bit>## (diagnostics/dimension-reference.md, matched by _DIMENSION_REQUEST_REPLY with self._where == ""), f"Light {self._where} autodiagnostic mask..." renders a double space ("Light autodiagnostic mask is ...").
    • On your question about where == "0": yes, please add *#1001*0*11*111111111111111101101111##, plus 4-digit 0014, routed 74#4#01, empty "", and a status-shaped WHO 1001 frame such as *1001*4*0## (end-of-interview marker in issue_466, which OWNEvent.parse also routes to OWNLightingDiagnosticEvent with diagnostic_mask is None). (is_general is False on *#1001*0*...## because base.py scopes is_general / is_group / is_area to who in (1, 2) — worth pinning if intentional.)
  4. Update tests/test_firmware_oracle_conformance.py (lines 155-162): test_emitted_own_frames_parseable_by_ownd already has a # 2. Semantic verification of WHO 1001 diagnostic state response block that still asserts isinstance(diag_msg, OWNMessage) and getattr(diag_msg, "event_content", ...) from before WHO 1001 had a parser. Please tighten it to assert isinstance(diag_msg, OWNLightingDiagnosticEvent), diag_msg.diagnostic_mask == "111110111111111111110111", and diag_msg.cleared_bits == (6, 21) like the surrounding WHO blocks.

  5. Docstrings, CHANGELOG.md, and trace references (no new trace PR needed):

    • No need to ingest a new trace PR for this — the 2026-10-24 F461 / MyHomeServer1 direct reads are fine as Hardware Verification notes, and we already have committed MyHOME plant traces covering both the physical WHAT 19 push path and non-WHAT 19 pushes:
      • issue_619/myhome_sweep_F453AV_all_2026-10-04T19-45-11.json (F453AV 3.0.14): five unloaded F414 dimmers (13, 14, 24, 31, 34) pushing *1*19*WHERE## + *#1001*WHERE*11*111110111111111111110111## ((6, 21)).
      • issue_466/myhome_trace_MH200_all_2026-09-26T21-00-00.json and issue_733/myhome_trace_MH200_dim1_probe_2026-10-20T19-22-47.json (MH200): WHERE 74 pushing *#1001*74*11*111110111111111111110111##.
      • issue_434/myhome_trace_MH200N_all_2026-09-29T18-31-59.json and issue_746/myhome_trace_MH200N_all_2026-10-17T14-28-20.json (MH200N 1.1.8): during area sweeps (*#1*4##, *#1*7##), 42 and 72 reply with normal *1*0*42## (OFF) and *1*1*72## / *1*0*72## (ON/OFF) followed 168–210 ms later by *#1001*42*11*111011111111111101101101## and *#1001*72*11*111011111111111101101101## (cleared_bits == (4, 17, 20, 23), no WHAT 19).
    • Please cite those committed fixtures in the PR table instead of "capture on file, to be ingested with the next trace PR" (and adjust the "rests on the oracle rows only" note), and broaden the OWNLightingDiagnosticEvent docstring + CHANGELOG.md entry so DIMENSION 11 isn't described as exclusively following *1*19*WHERE## (it is also answered on direct *#1001*WHERE*11## reads and pushed alongside normal WHAT 0 / WHAT 1 sweep replies on MH200N).

Non-blocking:

  • WHO 1001 spans Lighting and Automation (WHO 1 + WHO 2): OPEN.db (diagnostics/architecture.md) defines WHO 1001 for "Light and Automation system" (there is no WHO 1002, and the MyHomeServer1 cover diagnostic traces in issue_466 use WHO 1001 on covers). Keeping the class in lighting.py (EN_SYSTEM.who = 1) is fine, but a short note in the docstring and README.md catalog row will clarify that a WHO 1001 frame can also come from a WHO 2 shutter actuator.
  • Avoid recomputing cleared_bits: __init__ builds cleared = tuple(...) for _human_readable_log and discards it, then cleared_bits recomputes the tuple on every access; storing self._cleared_bits: tuple[int, ...] | None = None in __init__ avoids the duplicate pass.
  • PR description nits: under ## Testing, bullet 2 still says "on this branch all 8 pass" (13 now), and the Mermaid Diagnostic state label still says one value, all 0/1 without the 24-bit constraint from c42dc2f.

Mac Book and others added 2 commits October 26, 2026 22:37
… 0 WHERE, fixtures cited (review follow-up)

Review of 2026-10-26 20:32 on OpenWebNet-HA#145:

- DIMENSION 7 (`BITMASK_DIA_A`, OPEN.db) carries the same 24-bit mask as
  DIMENSION 11: MyHOME's device_health pairs (7, 11) and the MyHomeServer1
  cover diagnostic in MyHOME issue_466 sends `*#1001*0*7*1111…0111##` with
  the same bits as the live DIMENSION 11 reads. The decoder accepts both.
- WHO 1001 joins the WHOs whose WHERE may carry `michnovka#4#II` in
  `OWNMessage.interface`, so `interface`, `unique_id` (`1001-74#4#01`)
  and the log line cover an actuator behind an F422, as for WHO 1 / 2.
- The log subject handles an empty WHERE (the gateway's own diagnostic)
  without a double space; `cleared_bits` is stored once in `__init__`.
- Docstring, README catalog row and CHANGELOG: WHO 1001 is the diagnostic
  WHO of lighting and automation actuators alike; the mask is pushed after
  `*1*19*WHERE##` (oracle what19; MyHOME fixtures issue_619 F453AV,
  issue_466 / issue_733 MH200), pushed alongside normal `*1*0/1*WHERE##`
  sweep replies on an MH200N (issue_434, issue_746), answered on a direct
  read (live F461 / MyHomeServer1, 2026-10-24) and sent as DIMENSION 7 on
  WHERE 0 (issue_466).
- Tests: WHERE `0`, `0014`, routed `74#4#01`, empty, the status-shaped
  `*1001*4*0##`, DIMENSION 7 shapes, `is_general False` pinned; the
  firmware-oracle conformance block asserts the class, mask and cleared
  bits.
- Merged origin/master (050dc1d) into the branch (CHANGELOG conflict with
  OpenWebNet-HA#136 / OpenWebNet-HA#140 / OpenWebNet-HA#148 resolved, the WHO 1001 entry under "Added").

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

gdluck commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed pass. All five blocking points and the three notes are in 6014d03 (and the merge commit before it):

  1. Master: origin/master 050dc1d is merged into the branch (the CHANGELOG conflict with fix(connection): refuse WHO 4 dimension 22 in every send path #136 / feat(heating): OWNHeatingCommand.actuators_status(where) builder for *#4*WHERE*20## #140 / docs(changelog): describe send_to_gateway's dimension 22 refusal as it is #148 resolved, the WHO 1001 entry under "Added"). I merged rather than rebased so the pushed history stays as reviewed; say the word if you want a rebased single-commit branch instead.
  2. DIMENSION 7: the decoder accepts the mask on DIMENSION 7 (BITMASK_DIA_A) and 11 (_MASK_DIMENSIONS = (7, 11)); the issue_466 frame *#1001*0*7*111111111111111101101111## now gives cleared_bits == (17, 20) like the live DIMENSION 11 reads. Docstring, README row and CHANGELOG updated accordingly.
  3. Routed / empty / 0 WHERE: WHO 1001 joins the WHO list of OWNMessage.interface (base.py), so *#1001*74#4#01*11*…## gives interface == "01", unique_id == "1001-74#4#01" and the log "Light 74 on interface 01 autodiagnostic mask is …"; the empty-WHERE service form logs "The lighting / automation system autodiagnostic mask is …" (no double space); tests pin 0, 0014, 74#4#01, the empty form, DIMENSION 7 shapes, the status-shaped *1001*4*0## (class with diagnostic_mask None) and is_general is False on WHO 1001 (pinned as the existing WHO 1 / 2 scoping, not changed).
  4. tests/test_firmware_oracle_conformance.py: block 2 asserts isinstance(diag_msg, OWNLightingDiagnosticEvent), diagnostic_mask == "111110111111111111110111" and cleared_bits == (6, 21).
  5. Docstrings / CHANGELOG / references: the committed fixtures are cited (issue_619 F453AV, issue_466 / issue_733 MH200, issue_434 / issue_746 MH200N, issue_466 DIMENSION 7 on WHERE 0), the docstring lists the four ways the mask is seen (after WHAT 19, 170-210 ms after normal sweep replies on MH200N, on a direct read, as DIMENSION 7), and the PR table's "capture on file" rows are hardware-verification notes only, as you suggested.

Notes: WHO 1001 is described as the diagnostic WHO of lighting and automation actuators alike (docstring, README row); cleared_bits is computed once in __init__; the PR description says 20 cases and the diagram label carries the 24-bit constraint.

Checks: 2124 passed, 1 skipped; base.py and lighting.py at 100 % statements; ruff, mypy, scripts/sync_documentation.py --check and scripts/verify_library_standards.py clean. Kimi re-verified the cleared-bit positions of the three masks and the routed / empty-WHERE results against the base-class contract.

@GreenGrassBlueOcean
GreenGrassBlueOcean marked this pull request as ready for review October 26, 2026 21:14

@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 6014d03 — all five blocking points and the three follow-up notes are cleanly addressed:

  • DIMENSION 7 (BITMASK_DIA_A) and DIMENSION 11 both decode 24-bit 0/1 masks while non-mask shapes fail closed (diagnostic_mask is None, cleared_bits is None).
  • WHO 1001 joins OWNMessage.interface (74#4#01 -> interface == "01", unique_id == "1001-74#4#01"), empty-WHERE logging has no double space, and tests/test_firmware_oracle_conformance.py asserts OWNLightingDiagnosticEvent, diagnostic_mask, and cleared_bits == (6, 21).
  • Verified against all 125 unique WHO 1001 frames in the MyHOME trace corpus (9 masks across MH200, MH200N, F453AV, and MyHomeServer1 decode cleanly; 21 non-mask events fail closed; 95 requests/writes remain OWNCommand), with 100% statement/branch coverage on OWNLightingDiagnosticEvent and all 8 CI checks green.

LGTM!

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit 12fef0c into OpenWebNet-HA:master Oct 27, 2026
8 checks passed
GreenGrassBlueOcean pushed a commit that referenced this pull request Nov 2, 2026
OWNd/__init__.py: __version__ = "2.0.0b12". CHANGELOG.md: [Unreleased]
promoted to [2.0.0b12] - 2026-11-01 (every PR merged since v2.0.0-b11:
#134, #135, #136, #137, #138, #139, #140, #142, #145, #147, #148, #150,
#151) with a fresh empty [Unreleased] header.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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