Repository navigation
fix(automation): WHO 2 builders validate WHERE with require_address_where, ASCII-only priority string; WHO 2 / WHO 9 documentation and golden-corpus errata (Encyclopedia audit, group 2) - #159
Conversation
…ty and info Review-session audit automation_load_aux.md rows A1, A2, A3, A4, A5, A6, A7, A8, A9 (ENCY_CONFIRM AU-A1..AU-A9: A4, A5, A6, A7, A9 CONFIRMED, A1, A2, A3, A8 FUNCTIONAL-ONLY with the on-wire LN4660M2 frames as MyHOME traces). Encyclopedia who-2-automation/what.md#advanced-commands, #parameter-format and #observed-physical-transmitter-frames: *2*10#PRIORITY[#SELECTOR]*WHERE## is Advanced Stop, *2*11#STEP#PRIORITY[#SELECTOR]*WHERE## / *2*12#... advanced Up / Down with STEP 1..99 or 100 (end point) and a 3-bit priority; captured as *2*11#100#001#1*0##, *2*12#100#001#1*0##, *2*10#001#1*0##. They are now decoded into is_advanced_stop / is_advanced_up / is_advanced_down, step, moves_to_end_point, priority, priority_bitfield, selector and no longer copied into the Dimension 10 state (no position, no is_closed claim, no "None" in the log). what.md#state-values-are-not-commands and dimensions.md#shutter-status: *2*13*W## / *2*14*W## are not commands and fail closed (unknown_command_code). dimensions.md#shutter-priority and #shutter-information: Dimension 10 replies expose priority (int and 3-digit bit field) and info (0 / 12 / 13 / 14 / 15 with names). dimensions.md#resulting-state-traffic: the Dimension 11 operation and its F461 2.0.11 echo *michnovka#2*25*OpenWebNet-HA#11#001#1*50## (golden cover.evt.level.50.virtual.25.f461) are event traffic, routed by OWNMessage.parse to OWNAutomationEvent with target_level. report_shutter_level accepts states 10..14 only, emits the priority as the p1p2p3 bit field and logs 255 as an unknown position. addressing.md#where-forms: every WHO 2 builder refuses an empty or '*' WHERE. Golden corpus: dropped the legrand-spec fixture cover.dim.write.pos.75 (*michnovka#2*93*OpenWebNet-HA#10*75##): dimensions.md lists Dimension 10 as request / response / event only and the position write as *michnovka#2*WHERE*OpenWebNet-HA#11#PRIORITY*LEVEL## (ENCY_CONFIRM AU-A4, protocol/dimensions.md#writes). Relabelled cover.dim.write.tilt.30 as cover.dim.write.level.30.no_priority (Dimension 11 is absolute position; no slat-angle dimension exists, ENCY_CONFIRM AU-A5) and cover.evt.level.50.virtual.25.f461 as direction event; corpus.json regenerated with tools/golden/validate_corpus.py. README, the conformance matrix and the golden README no longer say "tilt angles" / "Slat Tilt". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ichnovka#3## Review-session audit automation_load_aux.md row L1 (ENCY_CONFIRM AU-L1: OWNd-SIDE, internal docstring / parser contradiction; ORACLE_CONFIRM row L1: CONFIRMED). load.py documented that bt_energia only accepts *michnovka#3## and *michnovka#3*## for general status, yet OWNMessage.parse('*michnovka#3*##') returned None and no builder existed. The 2026-11-03 oracle run (ORACLE_CONFIRM.md row L1) shows the documented *michnovka#3*0## (Encyclopedia who-3-load-management/README.md#status-requests) refused on 12/12 emulated gateways, *michnovka#3## out as $03AA001614 on 8 of them and *michnovka#3*## out on F454 / F459 only. base.py adds a WHO 3-only _LOAD_GENERAL_STATUS regex (*michnovka#3*## -> STATUS_REQUEST with where "") and OWNCommand.parse's WHO pre-screen accepts an empty last field; OWNLoadCommand logs "Requesting general load status." for both spellings and general_status() builds *michnovka#3##. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review-session audit automation_load_aux.md row X1 (ENCY_CONFIRM AU-X1: CONFIRMED). Encyclopedia who-9-auxiliaries/README.md#corpus-status establishes only *OpenWebNet-HA#9*WHERE##, *9*1*WHERE## (ON) and *9*0*WHERE## (OFF); "Unknown values remain unspecified rather than inferred" and the TS10 decoder "rejects other WHAT cases"; it "does not establish an auxiliary-address range". OWNAuxEvent keeps the openwebnet4j names for WHAT 2..10 (AUX_WHAT_NAMES_UNVERIFIED) but flags them as unverified in the log and in the new is_state_name_verified property; state_name exposes the name; channel is documented as the WHERE as sent. The README WHO 9 row (scripts/sync_documentation.py catalog) no longer says "aux 1-9". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…re (decision D56, D21) Precedent gate for report 21 rows A6 and A8. A6: `OWNMessage.parse` no longer routes `*michnovka#2*WHERE*OpenWebNet-HA#11#PRIORITY[#SELECTOR]*LEVEL##` to `OWNAutomationEvent`. DIMENSION 11 is write-only for WHO 2 (MyHOME #797 review, D56) and MyHOME's pinned suite asserts `OWNAutomationCommand` for the scope echoes `*michnovka#2*michnovka#5*OpenWebNet-HA#11#001#1*50##` / `*michnovka#2*0*OpenWebNet-HA#11#001#1*50##` (tests/test_gateway.py test_scope_events_fire_only_for_state_frames), so a general / area / group go-to-level echo can never reach the WHO 2 motion event branch of gateway_events.py. The command object now decodes `target_level`, `priority`, `priority_bitfield`, `selector` and `is_go_to_level` (who-2-automation/dimensions.md#dimension-11---go-to-level); `OWNEvent.parse` called directly on the frame still exposes the same fields. Golden `cover.evt.level.50.virtual.25.f461` keeps `direction: dimension_write` (corpus.json regenerated with tools/golden/validate_corpus.py). A8: `report_shutter_level` emits an int priority as its decimal text, as before (`*michnovka#2*21*10*12*40*0*0##`, pinned by MyHOME tests/test_expose.py); the 3-digit `p1p2p3` bit field is the string form, which is still validated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ision D21)
Precedent gate for report 21 row A8: MyHOME tests/test_expose.py
(test_expose_infeasible_inputs_hardening) pins
`report_shutter_level("31", 40, state=10, priority="00001")` ->
`*michnovka#2*31*10*10*40*1*0##`, so the string form keeps its pre-existing
handling (up to three digits sent as given, otherwise the decimal int 0..255)
instead of requiring a 3-digit bit field. The state 10..14 / info / 255 log
parts of the row stay.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…iority bitfield rule (review follow-up) Review follow-up on report 21 rows L1, A2, A7/A8. - `OWNCommand.parse` pre-screen is back to `(?:\*.+)?##$`; the empty last field is admitted only through `_LOAD_GENERAL_STATUS` (`*michnovka#3*##`), so `*michnovka#2*##`, `*OpenWebNet-HA#13*##`, `*michnovka#4*##` stay `None` as on master. - `_decode_advanced`: the WHAT grammar fixes the first field as STEP (who-2-automation/what.md#parameter-format), so a single parameter is the STEP with no priority: `*2*11#100*31##` -> step 100, end point; `*2*11#50*31##` -> step 50. - `_priority_bitfield`: exactly three `0`/`1` characters are the bit field (what.md#parameter-format, dimensions.md#shutter-priority); the page records no shortened form, so `1`, `7`, `10` are decimal priorities with `priority_bitfield None`. `report_shutter_level(priority=10)` -> `*10*` parses back as priority 10, not bit field 010. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
… not the published event grammar The WHO 2 PDF §3.0.7.1 publishes the event-session frame as *michnovka#2*WHERE*OpenWebNet-HA#11#PRIORITY#LEVEL*SELECTOR## (LEVEL as the second # parameter); the F461 2.0.11 echoes the write form *michnovka#2*WHERE*OpenWebNet-HA#11#PRIORITY[#SELECTOR]*LEVEL##, which is what OWNd decodes. CHANGELOG and two docstrings say so. No behaviour change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Thanks for auditing WHO 2, WHO 3, and WHO 9 and explicitly noting in the PR description that the Encyclopedia's WHO 2 captures are MyHOME's own captures rather than independent traces of the advanced commands.
Under our standing rule — if we don't have real, committed on-wire plant traces from physical hardware, we don't change behaviour — please trim this PR to the non-empty-WHERE builder guards and pure documentation fixes, and revert the doc-derived runtime changes.
What can stay
- Non-empty
WHEREguards onOWNAutomationCommandbuilders (A5): Rejecting emptywhereinup,down,stop,set_position,status, andadvanced_status(*2*1*##,*#2**10##are NACKed12/12in the firmware oracle), while keepingOWNCommand.parsepermissive. - Documentation & "Slat Tilt" cleanup: Removing the unverified
"Slat Tilt"wording fromREADME.md,docs/protocol_conformance_matrix.md,scripts/sync_documentation.py, andtests/golden/README.md, plus docstring notes onWHO 2/WHO 9specification caveats.
Blockers — Behaviour changes without committed plant traces (please revert)
| # | Symbol / File | Behaviour change without real plant trace | Required action |
|---|---|---|---|
| 1 | OWNAutomationEvent (WHAT 10, 11, 12 & WHAT 13, 14) in OWNd/message/automation.py |
Changes state (10/11/12 -> None) and is_closed (False -> None) on WHAT 10 / 11 / 12, changes WHAT 13 / 14 from state = 13/14 (is_opening / is_closing) to unknown_command_code (None), and adds speculative step, moves_to_end_point, priority_bitfield, priority_safety/high/medium, and selector decoders from WHO_2.pdf (ownkb:claim:c007501, c007502) with no independent physical advanced-shutter plant traces. |
Revert OWNAutomationEvent runtime decoding and human_readable_log changes to master until independent on-wire captures of advanced WHO 2 shutter flows are committed. |
| 2 | OWNAutomationCommand.report_shutter_level & DIMENSION 10 decoding in OWNd/message/automation.py |
Rejects state=15 and info outside {0, 12, 13, 14, 15} in report_shutter_level (forcing edits to test_expose_builders_infeasible_inputs in tests/test_message_protocol.py), drops DIMENSION 10 state outside 10..14, and appends info_name / alters 255 human_readable_log strings without trace evidence. |
Revert report_shutter_level validation/log changes and DIMENSION 10 decoding changes to master. |
| 3 | cover.dim.write.pos.75 (*#2*93*#10*75##) in tests/golden/frames/who02_automation.yaml & tests/golden/corpus.json |
Deletes cover.dim.write.pos.75 from the golden corpus. |
Do not delete golden parser fixtures: keep the record in who02_automation.yaml / corpus.json and relabel it source: conflict with updated mcp_notes (the same pattern used in #157 for who18_energy.yaml). |
| 4 | _LOAD_GENERAL_STATUS in OWNd/message/base.py & OWNLoadCommand.general_status in OWNd/message/load.py (WHO 3) |
Adds *#3*## special-casing in OWNMessage / OWNCommand and adds OWNLoadCommand.general_status() (*#3##) with zero physical F520 / F521 / F522 / F523 load-management plant captures (empty-bus gateway forwarding does not establish device replies). |
Revert base.py and load.py runtime additions until we have a real WHO 3 load-management capture. |
| 5 | OWNAuxEvent.human_readable_log (WHO 9) in OWNd/message/auxiliary.py |
Appends (WHAT N, unverified name) to human_readable_log for WHAT 2..10. |
Revert the runtime human_readable_log change; keep the provenance caveat in AUX_WHAT_NAMES comments / OWNAuxEvent docstrings. |
…plant traces; keep the WHERE guards and docs Maintainer review of OpenWebNet-HA#159: without real, committed on-wire plant traces from physical hardware, runtime behaviour does not change. Spec PDFs, Encyclopedia prose and empty-bus gateway ACKs / echoes are documentation context; oracle NACKs justify builder guards only. Blocker 1 (OWNAutomationEvent WHAT 10/11/12, 13/14, DIMENSION 11): reverted to master. `state` again carries any command WHAT (10/11/12 included), WHAT 13/14 keep `state = 13/14` with `is_opening` / `is_closing`; no `unknown_command_code`. Removed the `step`, `moves_to_end_point`, `priority`, `priority_bitfield`, `priority_safety/high/medium`, `selector`, `info_name`, `is_command_not_executed`, `target_level`, `is_go_to_level` decoders on the event and on `OWNAutomationCommand`, the related log strings, the module-level constants and helpers, and their tests. Kept: the module docstring with the WHO 2 specification caveats, including the frame-shape note (F461 2.0.11 echoes the DIMENSION 11 write form `*michnovka#2*25*OpenWebNet-HA#11#001#1*50##`; the PDF publishes `*michnovka#2*WHERE*OpenWebNet-HA#11#PRIORITY#LEVEL*SELECTOR##`). Blocker 2 (report_shutter_level / DIMENSION 10): reverted to master. `state` 10..15 and `priority` / `info` 0..255 are accepted again, the level-255 log string is master's, DIMENSION 10 `state` outside 10..14 is decoded as before; `tests/test_message_protocol.py` restored from master. Blocker 3 (golden `cover.dim.write.pos.75`): restored, not deleted, relabelled `source: conflict` with `mcp_notes` explaining that the PDF / Encyclopedia define no DIMENSION 10 write (the position write is DIMENSION 11) and that no committed plant trace carries the frame, so it pins parser shape only (the pattern OpenWebNet-HA#157 used for who18_energy.yaml). corpus.json regenerated from the YAML files (no repository tool exists; same json.dumps(indent=2) layout as the committed file). The `cover.dim.write.tilt.30` -> `cover.dim.write.level.30.no_priority` relabel is kept (documentation). Blocker 4 (WHO 3): `_LOAD_GENERAL_STATUS` in base.py, the `OWNCommand.parse` pre-screen change and `OWNLoadCommand.general_status()` reverted to master with their tests; `*michnovka#3*##` is rejected as before. Blocker 5 (WHO 9): the runtime `(WHAT N, unverified name)` log suffix, `state_name` and `is_state_name_verified` reverted; master's log branches restored verbatim. Kept: the provenance caveat as comments on `AUX_WHAT_NAMES` / `AUX_WHAT_NAMES_UNVERIFIED` and the `OWNAuxEvent` docstring. Kept: the non-empty-WHERE guards on `status`, `get_shutter_status`, `raise_shutter`, `lower_shutter`, `stop_shutter` (`*2*1*##` and `*michnovka#2**10##` NACKed 12/12 in the firmware oracle; `OWNCommand.parse` stays permissive, pinned by a test) and the "Slat Tilt" / "tilt angles" removal in README.md, docs/protocol_conformance_matrix.md, scripts/sync_documentation.py and tests/golden/README.md. The generated WHO 2 catalog description no longer claims advanced-command decoding. CHANGELOG entries for the reverted items removed; the kept ones say no behaviour change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Round 2 (review of 2026-11-09) applied in
Checks: Agreed on the follow-up: no separate PR for Round 1 (2026-11-04), applied in `fe3c463`
|
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Checked fc34e2c against current master (32cca9e6), own-firmware-oracle, the committed MyHOME traces (issue_445, issue_466, and MyHOME#865), and downstream MyHOME consumers (cover.py, expose.py, gateway_events.py).
Does this PR solve a real problem?
- Runtime (
OWNAutomationCommandWHEREguards): Marginal API hygiene only — fixes no bug or crash. Onmaster,status("")(*#2*##),raise_shutter("")(*2*1*##),lower_shutter("")(*2*2*##), andstop_shutter("")(*2*0*##) already parse withis_valid is Falseand are blocked before touching the socket or UART byOWNCommandSession.send(OWNd/connection/command_session.py:283) andAsyncSerialTransport.send(OWNd/transport/serial.py:173) withOWNProtocolError(..., reason="invalid_frame"). The onlyis_valid is Trueframes previously reachable from those builders wereget_shutter_status("")(*#2**10##, accepted bybase.pyonly because WHO 13 uses*#13**0##),where="*"(*2*1**##, accepted only because WHO 5 uses*), and delimiter injection (status("31*10")->*#2*31*10##). EveryMyHOMEcaller (cover.py,expose.py) passes a pre-validatedWHERE, and unlike WHO 1*#1*##(MH202crash, #155) or WHO 4Dimension 22(bt_termocrash, #156), there is no firmware crash vector here. Fail-early builder validation is fine for parity with #155, provided it usesmaster'srequire_address_whererather than the supersededrequire_non_empty_where. - Documentation & golden metadata (
WHO 2): Yes (errata cleanup). OpenWebNet WHO 2 has no wire dimension for slat tilt (MyHOME's Venetian blind tilt incover.pyis purely client-side timed motor-pulse estimation over standardUP/DOWN/STOP), andWHO_2.pdfdefines noDimension 10write (*#2*93*#10*75##). Relabellingcover.dim.write.pos.75assource: conflict, renaming/clarifyingcover.dim.write.tilt.30, and removing"Slat Tilt"/"tilt angles"from WHO 2 wire-protocol docs fixes real documentation drift. - WHO 9 module globals (
AUX_WHAT_NAMES,AUX_WHAT_NAMES_UNVERIFIED): No. Dead unexported globals referenced nowhere inOWNdorMyHOME.
Summary of Required Changes
| # | Severity | Location | Issue | Required Action |
|---|---|---|---|---|
| 1 | Blocker | CHANGELOG.md:8, OWNd/message/automation.py:24-25, 177-178, tests/test_who2_audit_fixes.py:4-5, 76 |
States 6× as empirical fact that *2*1*## and *#2**10## "are NACKed by all twelve emulated gateways in the firmware oracle" — neither frame exists in own-firmware-oracle or tests/golden/firmware_oracle.json. |
Remove the unverified 12/12 firmware-oracle claim everywhere (cite WHO_2.pdf / Encyclopedia who-2-automation/addressing.md#where-forms), or actually replay *2*1*##, *#2*##, and *#2**10## in own-firmware-oracle first and cite the real suite. |
| 2 | High | OWNd/message/automation.py:38, 185-228, 264 |
Uses require_non_empty_where instead of master's require_address_where (#155): raise_shutter(True) passes mypy --strict (bool <: int) and builds invalid *2*1*True## (where=None, log "Raising shutter None."); status(None) -> *#2*None##; raise_shutter("31##") -> *2*1*31####; raise_shutter("31 1") -> *2*1*31 1##. |
Switch all seven OWNAutomationCommand builders (set_shutter_level and report_shutter_level included) to require_address_where, and test bool, None, "31##", "31 1", and "31\n". |
| 3 | Medium | OWNd/message/scenario.py:75-94 |
AUX_WHAT_NAMES and AUX_WHAT_NAMES_UNVERIFIED are dead globals (not in OWNd/message/__init__.py __all__, not used by OWNAuxEvent.__init__, not imported anywhere). |
Delete both dicts; keep the provenance caveat in the OWNAuxEvent / channel docstrings only. |
| 4 | Medium | OWNd/message/automation.py:4-5, 11-12, tests/test_who2_audit_fixes.py:8 |
Claims "no committed on-wire plant trace exercises the advanced forms" (contradicted by MyHOME#445 and MyHOME#466) and states unqualifiedly that 13 / 14 are "only DIMENSION 10 state values" (contradicted by F461 2.0.11 translating *2*13*05## / *2*14*05## in MyHOME#865). |
Reword both docstring notes to match the actual committed plant captures. |
| 5 | Medium | OWNd/message/automation.py:67-95, 280-286 |
Non-ASCII digit leak vs #155 / #156: OWNAutomationEvent (DIMENSION 10) decodes fullwidth Unicode digits (*#2*21*10*12*40*001*14## -> state=12, current_position=40), and report_shutter_level("31", 40, 10, "001", 0) emits non-ASCII wire bytes because .isdigit() lacks .isascii(). |
At minimum harden report_shutter_level's string priority check with .isascii() (and either guard DIMENSION 10 with .isascii() and .isdigit() for #155/#156 parity or leave OWNAutomationEvent untouched if keeping zero decode diff here). |
| 6 | Blocker | Branch merge state + PR Title / Body + tests/test_who2_audit_fixes.py |
Branch is 30 commits behind master (mergeable_state: "dirty", conflicts in 6 files); PR title, summary, changes list, state diagram, and test count ("79 cases") still describe the reverted b931362 design; test file retains private scratch tags (rows A4, A5, A9, D56, test_a4_a5_*). |
Rebase onto master, rewrite PR title and body to match the actual diff, re-run sync_documentation.py / corpus validation (verify fixture count in tests/golden/README.md), and strip scratch audit labels from tests/test_who2_audit_fixes.py. |
Detailed Notes & Answer on the Follow-Up Proposal (#6633949587 / MyHOME#865)
-
Unverified firmware-oracle citation (
*2*1*##,*#2**10##):- Checked
own-firmware-oracle/suites/*.cases(ownd-audit-frames.casesonly tested WHO 1*#1*##, WHO 3, WHO 17, WHO 18, WHO 22;trace-corpus.casesonly tested populated WHO 2WHEREvalues),own-firmware-oracle/results/**/*.tsv, andtests/golden/firmware_oracle.json: zero WHO 2 empty-WHEREframes have been replayed. As your original PR body noted ("The WHO 2 rows are decode-side"), the12/12parenthetical from Round 1 review text was never replayed in the oracle and must not be recorded as an oracle verdict in docstrings, tests, orCHANGELOG.md.
- Checked
-
Corrections on the cited
MyHOMEtraces (issue_445,issue_466, andMyHOME#865):issue_445vsissue_466:issue_445(MyHomeServer1+LN-4660M2) carries centralized GeneralWHERE=0frames (*2*11#100#001#1*0##,*2*12#100#001#1*0##,*2*10#001#1*0##), not*2*11#1#001*31##/*2*12#1#001*31##. TheWHERE=31step frames (10%,2%,1%) are HA-originated commands inissue_466/config_entry-myhome_F454_step_commands.json(whichF454echoed as*2*1000#12#10#001#1*31##, while the actuator at31reported standard*2*2*31##,*2*0*31##, andDimension 10*#2*31*10*12*40*001*0##->*#2*31*10*10*30*001*0##).MyHOME#865(f461_live_2026-11-04/F461_full_debug_log_2026-11-04T15-45-56.txt): Address05is a basic timedBT-F411/2relay shutter, not an advanced actuator. Compare lines 583–596 (*2*2*05##-> gateway*2*1000#0*05##, actuator*2*0*05##, gateway*2*1000#2*05##, actuator*2*2*05##) against lines 607–650 (*2*11#1#001*05##,*2*12#1#001*05##,*2*10#001#1*05##,*2*13*05##,*2*14*05##): on all five advanced commands, only theF461gateway'sbt_automdaemon emitted*2*1000#...*05##command translations — theF411/2at05emitted zero status frames and ignored every one of them. (Please also correctf461_live_2026-11-04/README.md:56inMyHOMEso gateway command translation is not conflated withF411/2actuator support.)WHAT 13andWHAT 14onF461 2.0.11: Lines 637–650 of that same log showF461 2.0.11ACKing*2*13*05##and*2*14*05##and translating them onto the monitor session as*2*1000#13#001#1*05##and*2*1000#14#001#1*05##(while NACKing*2*15*05##). That confirmsbt_automtreatsWHAT 13andWHAT 14as valid command WHATs despiteWHO_2.pdflisting13/14only underDimension 10states — directly illustrating why failing closed on13/14from the PDF alone would have broken real gateway traffic.
-
Should a separate PR re-propose
OWNAutomationEventadvanced-WHAT decoding?- No. Downstream
MyHOME(cover.pyandgateway_events.py) already handlesissue_445's*2*10/11/12#...*0##frames throughis_opening/is_closing, already accounts foreffective_what in (1, 2, 11, 12, 13, 14)on1000#translations (cover.py:2576), and deliberately ignoresDimension 11frames (cover.py:2582-2590) because shutter position comes from the actuator'sDimension 10reports rather than command echoes. Pinning the existingissue_445/issue_466frames ascommunity-plant-capturegolden records againstmaster's current decoding is fine if desired, whereas adding unused properties (step,moves_to_end_point,priority_bitfield,selector) or changingstatetoNonesolves no real problem.
- No. Downstream
| ## [Unreleased] | ||
|
|
||
| ### Changed | ||
| - **WHO 2 (`OWNAutomationCommand.status`, `get_shutter_status`, `raise_shutter`, `lower_shutter`, `stop_shutter`)**: an empty, blank or `*` WHERE raises `ValueError` as `set_shutter_level` and `report_shutter_level` already did; `*2*1*##` and `*#2**10##` are NACKed by all twelve emulated gateways in the firmware oracle, so the builders no longer emit them. `OWNCommand.parse` stays permissive and no decoded value changes. |
There was a problem hiding this comment.
Neither *2*1*## nor *#2**10## has been replayed in own-firmware-oracle or tests/golden/firmware_oracle.json (ownd-audit-frames.cases only tested WHO 1 *#1*##). Drop the all twelve emulated gateways in the firmware oracle claim here and in automation.py / test_who2_audit_fixes.py unless those frames are actually added to own-firmware-oracle and replayed, and update this bullet to reflect require_address_where across all seven OWNAutomationCommand builders.
|
|
||
| Specification caveats (documentation context only; the decoding below is the | ||
| long-standing behaviour and is not changed on the strength of these notes, as | ||
| no committed on-wire plant trace exercises the advanced forms): |
There was a problem hiding this comment.
Two factual fixes needed in this docstring block:
- Line 5 ("no committed on-wire plant trace exercises the advanced forms"):
MyHOMEhas committed traces exercising*2*11#100#001#1*0##,*2*12#100#001#1*0##,*2*10#001#1*0##(issue_445,MyHomeServer1+LN-4660M2) and*2*11#10#001*31##/*2*12#10#001*31##/*2*12#2#001*31##/*2*12#1#001*31##(issue_466,F454+1000#echoes +Dimension 10replies). Reword to reflect what is vs isn't captured on-wire. - Lines 11–12 (
13/14"listed only as DIMENSION 10 state values") & lines 24–25: note that whileWHO_2.pdflists13/14only underDimension 10, physicalF461 2.0.11(bt_autom) ACKs*2*13*W##/*2*14*W##and translates them as*2*1000#13#001#1*W##/*2*1000#14#001#1*W##(MyHOME#865), and remove the un-replayedfirmware oracle NACKs *2*1*## / *#2**10##claim on lines 24–25.
| @classmethod | ||
| def status(cls, where: str | int) -> OWNAutomationCommand: | ||
| message = cls(f"*#2*{where}##") | ||
| target = require_non_empty_where(where) |
There was a problem hiding this comment.
After rebasing onto master, use require_address_where(where) (added in #155) instead of require_non_empty_where(where) across all seven OWNAutomationCommand builders (status, get_shutter_status, raise_shutter, lower_shutter, stop_shutter, set_shutter_level, and report_shutter_level). require_non_empty_where still lets bool (raise_shutter(True) passes mypy --strict and builds invalid *2*1*True##), None, "31##", and "31 1" slip through. Also drop the un-replayed firmware-oracle claim in the class docstring above (lines 177–178).
|
|
||
| #: WHAT names the Encyclopedia establishes for WHO 9 (``who-9-auxiliaries/README.md#corpus-status``): | ||
| #: ``*9*1*WHERE##`` ON and ``*9*0*WHERE##`` OFF only. | ||
| AUX_WHAT_NAMES: dict[int, str] = {0: "OFF", 1: "ON"} |
There was a problem hiding this comment.
Neither AUX_WHAT_NAMES nor AUX_WHAT_NAMES_UNVERIFIED is exported in OWNd/message/__init__.py (__all__), referenced by OWNAuxEvent.__init__ below, or used anywhere else. Please remove both unused module globals and keep the provenance caveat in the OWNAuxEvent / channel docstrings only.
| @@ -0,0 +1,98 @@ | |||
| """WHO 2 audit follow-ups that change no decoded behaviour (review-session rows A4, A5, A9). | |||
There was a problem hiding this comment.
Strip the private review-session scratch tags (review-session rows A4, A5, A9, # ── A4 / A5:, (gate, D56), test_a4_a5_*, test_a9_*) and the un-replayed NACKed 12/12 in the firmware oracle comment here and on line 76, and expand the rejected-WHERE parametrization to cover True, False, None, "31##", "31 1", and "31\n" once switched to require_address_where.
… tree as 9dc1e45) The result is byte-identical to the single rebased commit 9dc1e45 (kept locally as backup/who2-rebased-9dc1e45): WHO 2 builders use require_address_where, ASCII-only priority string, un-replayed oracle claim and dead WHO 9 globals removed, docstrings on the committed captures, scratch tags stripped, corpus regenerated (166), golden README count 166. A history rewrite was not possible from this session, so the branch is brought up to master by merge; a squash merge gives the same tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Requested by A
Summary
Review round 2 (GreenGrassBlueOcean, 2026-11-09) applied in
4cdf1a8, a merge ofmaster32cca9einto the branch whose tree is byte-identical to the single rebased commit9dc1e45(a history rewrite was not possible from the session that pushed; a squash merge gives the same result, and the rebased commit can be force-pushed instead on request).git diff masteris the same either way. The runtime change is argument validation on the WHO 2 builders plus one wire-hygiene fix; everything else is documentation and golden-corpus metadata. No decoded value, log line of a valid frame or accepted parser domain differs frommaster.OWNAutomationCommand: all seven builders (status,get_shutter_status,raise_shutter,lower_shutter,stop_shutter,set_shutter_level,report_shutter_level) pass their WHERE throughmaster'srequire_address_where(fix(who1): crash guards for empty DIMENSION values, non-empty WHERE on WHO 1 / WHO 17 builders, DIMENSION 4 status_level / status_speed #155), as the WHO 1 builders do. A WHERE that is not astr/int, abool,None, empty, blank, or containing*,##or inner whitespace raisesValueError(leading / trailing whitespace is stripped). Before:raise_shutter(True)built*2*1*True##(log "Raising shutter None."),status(None)*#2*None##,raise_shutter("31##")*2*1*31####,raise_shutter("")*2*1*##,raise_shutter("*")the valid-looking*2*1**##,get_shutter_status("")the valid-looking*#2**10##,status("31*10")the DIMENSION 10 read*#2*31*10##.OWNCommand.parsestays permissive (*2*1*##still parses as an invalidOWNAutomationCommand).report_shutter_level: a stringpriorityis kept verbatim ("001") only when it is ASCII digits."001"passedstr.isdigit()and reached the wire as*#2*31*10*10*40*001*0##; it is now encoded throughint(*1*).OWNAutomationEventis untouched (zero decode diff, as discussed in the review).OWNd/message/automation.pydescribes the WHO 2 advanced-command, DIMENSION 10 / 11 and WHERE grammar against the committed MyHOME captures: the LN4660M2 centralised control of a MyHomeServer1 plant putting*2*11#100#001#1*0##/*2*12#100#001#1*0##/*2*10#001#1*0##on the bus (issue_445); an F454 echoing Home Assistant's relative steps*2*11#STEP#001*31##/*2*12#STEP#001*31##(STEP 1, 2, 10) as*2*1000#11#STEP#001#1*31##/*2*1000#12#STEP#001#1*31##while the actuator reports*2*1*31##/*2*2*31##, DIMENSION 10 and its own*2*0*31##(issue_466,config_entry-myhome_F454_step_commands.json); an F461 2.0.11 ACKing*2*13*05##/*2*14*05##and translating them as*2*1000#13#001#1*05##/*2*1000#14#001#1*05##while the F411/2 at05emits nothing for any of the five advanced commands (f461_live_2026-11-04, test(f461): live F461 2.0.11 full debug log and bus sweep, 2026-11-04 (released OWNd 2.0.0b13) MyHOME#865). The un-replayed "NACKed by all twelve emulated gateways" claim is gone from the code, the tests and the CHANGELOG: neither*2*1*##nor*#2**10##is inown-firmware-oracle, and the guards are presented as argument validation only.AUX_WHAT_NAMES/AUX_WHAT_NAMES_UNVERIFIED(unused) are removed; the provenance caveat on the WHAT 2..10 names lives in theOWNAuxEventandchanneldocstrings only. Log strings unchanged.cover.dim.write.pos.75(*#2*93*#10*75##) relabelledsource: conflict(WHO_2.pdf / Encyclopediawho-2-automation/dimensions.mddefine no DIMENSION 10 write; no committed trace carries the frame; it pins parser shape only);cover.dim.write.tilt.30renamedcover.dim.write.level.30.no_priority(DIMENSION 11 is absolute position; WHO 2 has no slat-angle dimension). README,docs/protocol_conformance_matrix.md,scripts/sync_documentation.pyandtests/golden/README.mdno longer say "tilt angles" / "Slat Tilt"; the golden README count is the actual 166 (tools/golden/validate_corpus.py: 11 files, 166 frames, PASS).Review follow-up (round 2, 2026-11-09)
12/12firmware-oracle claimCHANGELOG.md,automation.py(module and class docstrings) andtests/test_who2_audit_fixes.py; the docstring now says explicitly that neither frame has been replayed inown-firmware-oracle.require_non_empty_where->require_address_whereTrue,False,None,12.5,b"31",["31"],"31##","31 1","31\t1","31\n1"(rejected) and" 0 ","31\n"(stripped and accepted, asrequire_address_wheredoes for WHO 1).AUX_WHAT_NAMES/AUX_WHAT_NAMES_UNVERIFIEDOWNAuxEvent/channeldocstrings.issue_445,issue_466andf461_live_2026-11-04as described above; 13 / 14 noted as ACKed and translated by the F461 2.0.11.report_shutter_levelpriority string requires.isascii();OWNAutomationEventleft untouched (zero decode diff).master32cca9e(merge4cdf1a8, tree = rebased commit9dc1e45); title and body rewritten to the actual diff;test_a4_a5_*/test_a9_*/rows A4, A5, A9/D56labels removed;sync_documentation.py --check,verify_library_standards.py,validate_corpus.py(166) re-run.On the follow-up question: agreed, no separate PR re-proposing
OWNAutomationEventadvanced-WHAT decoding. The MyHOMEf461_live_2026-11-04/README.mdwording that conflates the F461's1000#translations with F411/2 actuator support will be corrected in MyHOME, not here.Testing
ruff check OWNd tests setup.py scripts,mypy OWNd tests/typing,python scripts/verify_library_standards.py,python scripts/sync_documentation.py --check,python tools/golden/validate_corpus.py(166 frames, PASS).python -m pytest -q: 3194 passed;tests/test_who2_audit_fixes.py: 111 cases.v2-phase1-architecture(6c50e358) test suite with this branch onPYTHONPATH, the waymyhome-conformanceruns it: see the follow-up comment for the result line.🤖 Generated with Claude Code