Skip to content

feat(cen): parse and build WHO 25 scenario-plus frames (WHAT 11-15) - #91

Merged
GreenGrassBlueOcean merged 2 commits into
OpenWebNet-HA:masterfrom
gdluck:fix/who25-scenario-plus
Oct 11, 2026
Merged

GreenGrassBlueOcean merged 2 commits into
OpenWebNet-HA:masterfrom
gdluck:fix/who25-scenario-plus

Conversation

@gdluck

@gdluck gdluck commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Support WHO 25 Scheduled Scenario PLUS (WHAT 11–15: on, off, increase, decrease, stop), implementing OWNScenarioPlusEvent and OWNScenarioPlusCommand with builders turn_on, turn_off, increase, decrease, stop.

Companion PR to OpenWebNet-HA/MyHOME#671.


1. What is the problem?

In Legrand / BTicino MyHOME installations, WHO 25 carries not only CEN+ pushbuttons (WHAT 21..28) and dry contact / IR events (WHAT 31..32), but is also used by scenario programmers and touch screens (MH200, MH200N, MH202, MyHomeServer1, H4890, TS10 libqtdevices) for Scheduled Scenario PLUS control:

  • *25*11#0*WHERE## (turn_on / start)
  • *25*12*WHERE## (turn_off / stop)
  • *25*13#0#5*WHERE## (increase)
  • *25*14#0#5*WHERE## (decrease)
  • *25*15*WHERE## (stop)

Prior to this PR, frames in the 11–15 range parsed as bare OWNEvent or OWNCommand without type information, action parsing, parameter validation, or builder factories (the PR 82 rule: WHO 25 WHATs other than 21-28 and 31/32 were treated as generic unknown events).


2. How is it solved?

  1. Typed Message Classes (OWNd/message/cen.py):
    • OWNScenarioPlusEvent: exposes action (on, off, increase, decrease, stop), object (scenario WHERE address), and push_button / parameters.
    • OWNScenarioPlusCommand: builder methods turn_on(where), turn_off(where), increase(where), decrease(where), stop(where).
  2. Parser Routing:
    • _parse_who25_event and _parse_who25_command route WHAT 11–15 to OWNScenarioPlusEvent and OWNScenarioPlusCommand.
    • Existing CEN+ (WHAT 21..28) and dry contact (WHAT 31..32) routing remains untouched.
    • Tolerates both plain client WHERE addresses (11) and gateway-emitted 1xx addresses (*25*12*1xx##).
  3. Exports: Exported from OWNd/message/__init__.py.

3. How is it tested?

  1. Unit Test Suite (tests/test_who25_scenario_plus.py):
    • Tests all 5 actions (on, off, increase, decrease, stop) for parsing and command builder generation.
    • Tests parameter handling (#0, #0#5), address tolerance (11 vs 1xx), and fallback to bare OWNEvent for unrecognized WHAT values.
    • Updates tests/test_issue_77_firmware_findings.py fix 9 to expect the typed OWNScenarioPlusEvent.
    • Full suite passes: 1,242 passed, 1 skipped.
  2. Firmware Emulation & Oracle (bt_luci):
    • Verified against ARM binaries under QEMU on simulated SCS bus (MyHomeServer1 028206, MH202 010024, F454, F459):
      Frame Reply Bus body Monitor port echo
      *25*11#0*11## ACK B1 01 93 00 *25*11#0*11##
      *25*12*11## ACK B1 01 94 00 *25*12*11##
      *25*13#0#5*11## ACK B1 01 95 40 *25*13#0#5*11##
      *25*14#0#5*11## ACK B1 01 96 40 *25*14#0#5*11##
      *25*15*11## ACK B1 01 97 00 *25*15*11##
  3. Touchscreen Client Reference:
    • Matches Legrand libqtdevices tag TS10_1_0_23 (scenario_device.cpp:36-41).

4. Where are the traces?

  1. Authentic Physical Plant Trace Captured & Verified:

    • Contributed by @TheDarkWizard on MyHOME issue #466 (comment 6085724930) from a physical BTicino MyHomeServer1 (FW 2.87.13).
    • In-tree trace PR: OpenWebNet-HA/MyHOME#673 (tests/fixtures/traces/issue_466/config_entry-myhome_MyHomeServer1_who25_scenario_plus.json).
    • Deterministic replay test: tests/test_issue_466_myhomeserver1_scenarioplus_trace.py (500 frames replayed through Home Assistant event dispatcher without exceptions).
  2. On-Wire Evidence:

    • [tx] *25*11#0*11## (2026-10-09T17:14:12.215385+00:00): Scenario Plus ON command sent to physical gateway.
    • [rx] *25*11#0*11## (2026-10-09T17:14:12.298174+00:00): Acknowledged and placed on physical SCS bus (translating to bus opcode B1 01 93 00), echoing back on monitor session 82.8 ms later.
    • Gateway status request: [tx] *#25*11## (2026-10-09T17:14:12.300230+00:00).
    • Protocol confirmation on OFF: User also attempted [tx] *25*12#0*11##. Because in OpenWebNet and bt_luci OFF takes no #0 parameter (*25*12*WHERE##), the gateway did not echo 12#0, confirming the exact grammar distinctions implemented in this PR.

OWNd returned a bare OWNEvent for *25*11#0*W## .. *25*15*W##. These are
the five scenario-plus commands of libqtdevices TS10_1_0_23
(scenario_device.cpp:37-41: "11#0" on, "12" off, "13#0#5" increase,
"14#0#5" decrease, "15" stop; sent by ScenarioPlusDevice, :163-186).
The gateway firmware (bt_luci on MyHomeServer1, F454, F459) accepts all
five with a bus frame (B1 01 93 00 / 94 00 / 95 40 / 96 40 / 97 00) and
echoes each on the monitor port; the emitted corpus has *25*12*1xx## and
*25*15*1xx## 64 times each.

Adds OWNScenarioPlusEvent (action) and OWNScenarioPlusCommand builders,
routes WHAT 11-15 to them in the WHO 25 dispatchers, and updates the
PR 82 test that expected a bare event for them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018oHp1P4vxxoFnaLUUZpbpZ
@gdluck
gdluck force-pushed the fix/who25-scenario-plus branch from f3a0047 to 44b6db6 Compare October 7, 2026 17:22
@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!

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

Review: WHO 25 Scenario-Plus Frames (WHAT 11–15)

Thanks @gdluck for digging into this! Grounding these frames in the BTicino 10" Touch Screen client (libqtdevices tag TS10_1_0_23, scenario_device.cpp:37-41, Object 406 "Scheduled scenario PLUS") and correlating them with bt_luci's translation to SCS opcodes 93–97 is excellent protocol archeology.

Before merging this into OWNd, there are two structural prerequisites and two code fixes to address:

1. Authentic Bus Trace in MyHOME (PR Required)

We audited the trace corpus in MyHOME/tests/fixtures/traces/ across all gateway captures (MH200N, MH202, MyHomeServer1, F454). All existing authentic on-wire captures under WHO 25 consist exclusively of:

  • CEN+ interactions: *25*21#... through *25*24#...
  • Dry contacts / IR: *25*31#1*... / *25*32#1*...

Currently, there are zero authentic customer on-wire traces demonstrating *25*11..15 in the wild. To ground this change in empirical evidence and protect against regressions, we need an authentic bus capture (submitted as a trace fixture PR to MyHOME) showing a real device emitting or receiving these frames.

2. Companion PR in MyHOME

OWNd serves downstream integrations—primarily Home Assistant (MyHOME). To ensure that OWNScenarioPlusCommand and OWNScenarioPlusEvent are not left as dead factory code in OWNd, we need a companion PR in MyHOME demonstrating how these factory builders and parsed events are actually consumed (e.g. platform entities, device triggers, or services) so that we can work with factory functions.

3. Code Fixes in This PR

  1. Missing from __all__ in OWNd/message/__init__.py (Must Fix):
    OWNScenarioPlusCommand and OWNScenarioPlusEvent are imported on line 36 of OWNd/message/__init__.py, but are missing from __all__ (lines 187–192). This causes from OWNd.message import * to omit them and triggers typing/linter warnings in downstream consumers:

    # OWNd/message/__init__.py
    # CEN & Dry Contact (WHO 15 & 25)
    "OWNCENEvent",
    "OWNDryContactEvent",
    "OWNCENPlusEvent",
    "OWNCenCommand",
    "OWNDryContactCommand",
    "OWNCenPlusCommand",
    "OWNScenarioPlusEvent",     # <-- Add
    "OWNScenarioPlusCommand",   # <-- Add
  2. Consistent Human-Readable Logging in OWNScenarioPlusCommand:
    OWNScenarioPlusCommand sets _human_readable_log only inside its classmethod builders (turn_on, turn_off, etc.), but does not implement an __init__. When an incoming command frame is parsed via OWNCommand.parse("*25*11#0*11##"), its log message falls back to the generic Received command *25*11#0*11##. rather than Scenario plus 11: on.. Implementing an __init__ (matching OWNSceneCommand) ensures consistent logging regardless of whether the command was constructed or parsed.

@GreenGrassBlueOcean

GreenGrassBlueOcean commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Update on this PR:

  1. Bug fixes ready to cherry-pick:
    We fixed the missing __all__ export in OWNd/message/__init__.py and implemented OWNScenarioPlusCommand.__init__ so command frames parsed from the wire log consistently with Scenario plus {where}: {action}..
    The commit is ready on GreenGrassBlueOcean/OWNd:pr-91 (commit 4799743). Feel free to cherry-pick or merge it into your branch.

  2. Downstream companion PR opened in MyHOME:
    We opened companion PR OpenWebNet-HA/MyHOME#671 consuming OWNScenarioPlusEvent (bus event dispatch + CEN+ device auto-registration) and OWNScenarioPlusCommand (service myhome.send_scenario_plus_command), with full documentation and unit tests passing 100%.

  3. Status of Prerequisite 1 (Authentic User Trace — STRICTLY REQUIRED):

    • To be 100% clear: we will NOT bypass the authentic real-user plant trace requirement. Per maintainer policy (@fedem95 / @GreenGrassBlueOcean), this PR remains blocked until an authentic on-wire bus capture from a real installation is contributed to MyHOME.
    • The emulator/firmware oracle data provides helpful theoretical confirmation, but real user hardware evidence is non-negotiable before merging.
    • Community Callout Published: We have posted an active callout on MyHOME Issue #466 (comment 6085158753) requesting community members with physical scenario units (MH200N, MH202, MyHomeServer1) to capture a quick diagnostic trace of *25*11#0*11##. Once submitted, we will add it to the test corpus and proceed with merging.

@gdluck

gdluck commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

Cherry-picked your 4799743 onto the branch as feac14e (author kept): OWNScenarioPlusEvent / OWNScenarioPlusCommand are in __all__, and a parsed command now logs Scenario plus W: action. with an action property, same as the builders. Parsed and built frames for WHAT 11-15 check out. MyHOME #671 noted as the companion; the trace capture is the remaining prerequisite on your side.

@GreenGrassBlueOcean

Copy link
Copy Markdown
Contributor

📡 Physical Plant Trace Captured & Verified — Prerequisite Met! 🎉

Following up on our earlier status update: we have officially captured and verified an authentic on-wire trace on physical hardware!

Verified On-Wire Sequence:

  • [tx] *25*11#0*11## (2026-10-09T17:14:12.215385+00:00): Scenario Plus ON command sent to physical gateway.
  • [rx] *25*11#0*11## (2026-10-09T17:14:12.298174+00:00): Acknowledged and placed on physical SCS bus (matching the B1 01 93 00 opcode identified in the bt_luci firmware oracle), echoing back on the monitor session 82.8 ms later.
  • Gateway status query: [tx] *#25*11## (2026-10-09T17:14:12.300230+00:00).
  • Grammar confirmation on OFF: The trace also recorded [tx] *25*12#0*11##. Because in OpenWebNet and bt_luci OFF does not take a parameter (*25*12*WHERE##), the gateway did not echo 12#0, confirming the exact grammar distinctions implemented in this PR.

With this empirical evidence in-tree and deterministic replay tests passing, the physical trace prerequisite is 100% satisfied. This PR is now unblocked and ready to merge! 🚀

GreenGrassBlueOcean added a commit to OpenWebNet-HA/MyHOME that referenced this pull request Oct 9, 2026
…nd replay test (#466)

Captured from a physical BTicino MyHomeServer1 gateway (FW 2.87.13) by @TheDarkWizard on 2026-10-09 in response to the community call for real-world WHO 25 scenario plus traces (#466).

Validates gateway acceptance, SCS bus relay, and OpenWebNet monitor echo of Scheduled Scenario PLUS frame *25*11#0*11## (~82.8 ms RTT), unblocking OpenWebNet-HA/OWNd#91 and #671.

Co-authored-by: TheDarkWizard <9061870+TheDarkWizard@users.noreply.github.com>

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

Minor observations (non-blocking)

Event logs include parameters when present; Command logs do not. This matches the tests and is consistent enough.
Builders re-assign _human_readable_log after init already sets it correctly — harmless redundancy.
No explicit validation that parameters match the canonical forms (#0, #0#5). This is the right call given real firmware variance.

No security, correctness, or regression issues stand out. The code is clean, well-documented with source references, and follows project conventions.

@fedem95 fedem95 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The five WHO 25 WHAT 11–15 forms are covered by the builders, parser routing, exports, and regression tests. The physical trace and companion integration have been provided, and all checks pass.
Minor note: each builder resets _human_readable_log after init already sets it. This is redundant but harmless.

@GreenGrassBlueOcean
GreenGrassBlueOcean merged commit 179c87f into OpenWebNet-HA:master Oct 11, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants