Repository navigation
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
MyHomeServer1 README: "OWnd" typo; the alarm gate, the config-flow identification and the liveness probe rows name MyHOME OpenWebNet-HA#803, OpenWebNet-HA#804, OpenWebNet-HA#806. F461 README: the "What this capture backs" table extends to MyHOME OpenWebNet-HA#800-OpenWebNet-HA#806 (OpenWebNet-HA#802 the dimension 27 wire form, OpenWebNet-HA#803 *michnovka#5*0##, OpenWebNet-HA#804 the WHO 13 / 1013 identification replies, OpenWebNet-HA#806 the *OpenWebNet-HA#13**0## echo; OpenWebNet-HA#800 / OpenWebNet-HA#801 / OpenWebNet-HA#805 have no frame in this capture); it states that f461_live_2026-10-15 holds the dimension 22 reboot behind OWNd OpenWebNet-HA#136, the *4*311*2## / *4*311*#2## pair behind OWNd OpenWebNet-HA#137 and probe_area0 behind OWNd OpenWebNet-HA#141 / MyHOME OpenWebNet-HA#798; and one sentence says reply_time_s is measured while tx.t is reconstructed from the probe log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1683fd3 to
4f7423d
Compare
MyHomeServer1 README: "OWnd" typo; the alarm gate, the config-flow identification and the liveness probe rows name MyHOME #803, #804, #806. F461 README: the "What this capture backs" table extends to MyHOME #800-#806 (#802 the dimension 27 wire form, #803 *#5*0##, #804 the WHO 13 / 1013 identification replies, #806 the *#13**0## echo; #800 / #801 / #805 have no frame in this capture); it states that f461_live_2026-10-15 holds the dimension 22 reboot behind OWNd #136, the *4*311*2## / *4*311*#2## pair behind OWNd #137 and probe_area0 behind OWNd #141 / MyHOME #798; and one sentence says reply_time_s is measured while tx.t is reconstructed from the probe log. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
d6b3f47 to
31d44a9
Compare
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Code Review — Requesting Changes
The core state-model fix here is solid and solves a real safety/UX bug: starting MyHOMEAlarmControlPanel as None (unknown) instead of disarmed, restoring MyHOMEEntity.async_added_to_hass() (RestoreEntity + DeviceHealth + post-discovery poll deferral), and clearing restored: True only on frames with a real arm/disarm/alarm verdict (*5*8*, *5*9*/*5*2*/*5*0*, *5*12/15/16/17/31*) rather than *5*1*0## (activation) or zone frames.
Before this moves out of draft / merges, please address the following issues (inline details attached):
- Physical F454 regression & doc contradiction (
#311vs oracle020051): Adding"F454"unconditionally toALARM_STATUS_REFUSED_MODELScontradicts our committed live F454 + burglar-alarm capture intests/fixtures/plants/issue_311_f454/diagnostic_summary.json(lines 58–137,firmware: "2.0"), where[TX] *#5*#1##is answered on the bus by[RX] *5*11*#1##and the startup poll elicits*5*1*##, *5*5*##, *5*7*##, *5*9*##(routed byroute_keystoCENTRAL_UNIT_KEY = "0", confirmingalarm_0immediately on restart). It also contradictsdocs/configuration/alarm.md:16right above the edited section andown-firmware-oracle/tools/expected_divergences.yaml(F454020051vs field1.0.34/2.0/2.0.48+silent_bus_vs_populated_trace). - Shared-bus delegation, failover & alias normalization in
_status_request_refused:send_status_request()routes through_delegated_target(cmd)and_failover_target(cmd), whereas_status_request_refusedchecksself._gateway_handlerdirectly (suppressing*#5*0##on e.g. aMyHomeServer1primary +MH202secondary carryingWHO 5) and reads rawgateway.model(CONF_NAME) instead of canonicalprofile.model_name(missing SKU/short aliases like573992orMHS1). - State/docs polish:
- Guard
raw_state/state_codeupdates whilerestored: Truewhen a non-verdict frame (*5*1*0##,*5*11*#1##) arrives, and restrictasync_restore_last_stateto{STATE_DISARMED, STATE_ARMED_AWAY, STATE_TRIGGERED}so legacy pre-#609armed_homeentries cannot wedge the template alarm recipe. - Clarify the template alarm
availability:comment indocs/configuration/alarm.md:67-69now thatRestoreEntitymakesalarm_0immediately available after a restart (using the restored state).
- Guard
- Issue & PR citations:
#177(@Manfredi87, 2025-04-08) is a pre-v2 feature request on a MyHomeServer1 before WHO 5 existed — neitherF454nor the quote"the state is always disarmed after a restart"appears in#177(please clean up in PR title/body, commit message, release note, and comments).- In the PR body:
#799is already merged into based8bac157(notopen), andSkipping WHO=5 discovery: not supported by F461 profilecomes from_discovery_frames()+OWNdDEFAULT_SUPPORTED_WHOalready onv2-phase1-architecturerather than#794.
| # Gateways that NACK the WHO 5 status request ``*#5*0##`` (own-firmware-oracle | ||
| # ``results/<model>/<fw>/oracle/full/trace-corpus.tsv``: F450 020010, F453AV 030014, | ||
| # F454 020051, F459 020105, F460 020012, F461 020011, MyHomeServer1 028206 answer | ||
| # ``nack`` / ``silent``). Fallback for OWNd profiles that carry no | ||
| # ``supports_alarm_status_request`` flag yet; the flag belongs in OWNd (#381 pattern). | ||
| ALARM_STATUS_REFUSED_MODELS = frozenset({"F450", "F453AV", "F454", "F459", "F460", "F461", "MYHOMESERVER1"}) |
There was a problem hiding this comment.
Blocking — contradicts our live physical F454 trace (#311) and docs/configuration/alarm.md:16:
While emulated factory-blank F454 020051 returns nack / silent in own-firmware-oracle, our committed physical F454 (firmware: "2.0") + burglar-alarm trace in tests/fixtures/plants/issue_311_f454/diagnostic_summary.json (lines 58–137) proves a field F454 does forward WHO 5 status requests and return central-unit state:
- Lines 124–137:
[TX] *#5*#1##(14:17:38.658Z) →[RX] *5*11*#1##(14:17:38.719Z). - Lines 58–122: Startup poll returns
*5*1*##,*5*5*##,*5*7*##,*5*9*##,*5*11*#1##..#4##,*5*18*#5##..#8##,*5*15*#3##— androute_keys()routes empty-WHERE*5*9*##toCENTRAL_UNIT_KEY = "0", immediately confirmingalarm_control_panel.alarm_0's live state on restart!
own-firmware-oracle/tools/expected_divergences.yaml already records this divergence (F454 020051 vs field 1.0.34/2.0/2.0.48 on WHO 5/13, plus *#5*0## and *#5*#1## under silent_bus_vs_populated_trace), and docs/configuration/alarm.md:16 explicitly documents F454 answering polls with *5*1*##, *5*5*##, *5*7*##, *5*9*##.
Suppressing *#5*0## unconditionally for "F454" regresses live startup state confirmation on physical F454 plants where alarm_0 is already in the entity registry (leaving HA on an unconfirmed restored: True state if the alarm was armed/disarmed by transponder while HA was restarting). Since entity async_update() sends only a single command-session frame after initial discovery completes (and only when an alarm entity exists), please remove "F454" from ALARM_STATUS_REFUSED_MODELS (or gate by verified firmware revision if >= 2.0.51 genuinely dropped WHO 5 even with an alarm plant configured) and keep docs/configuration/alarm.md consistent.
| # The central unit takes no code over the bus. | ||
| self._attr_code_arm_required = False | ||
| self._attr_alarm_state = STATE_DISARMED | ||
| # Unknown until a bus frame or the restored state says otherwise (#177). |
There was a problem hiding this comment.
Misattributed #177 reference (here, in test_alarm_restore_unknown.py, .github/release-notes/2.0.0b16.md, commit message, and PR title/body): #177 (@Manfredi87, 2025-04-08) was a pre-v2 feature request on a MyHomeServer1 asking how to add an F4201 alarm before WHO 5 existed — neither F454 nor the quote "the state is always disarmed after a restart" appears in #177.
| async def async_restore_last_state(self, last_state: State) -> None: | ||
| """Restore the last known state, flagged as restored until a bus frame confirms it.""" | ||
| try: | ||
| state = AlarmControlPanelState(last_state.state) | ||
| except ValueError: | ||
| return | ||
| self._attr_alarm_state = state |
There was a problem hiding this comment.
AlarmControlPanelState(last_state.state) accepts every HA core enum value (including armed_home, which MyHOME removed in #609, as well as arming/pending). Restoring a stale armed_home from .storage/core.restore_state would leave the documented template panel (states(...) in ['disarmed', 'armed_away', 'triggered']) stuck unavailable.
Please restrict restoration to the states this entity actually emits ({STATE_DISARMED, STATE_ARMED_AWAY, STATE_TRIGGERED}).
| def _status_request_refused(self) -> bool: | ||
| """Whether this gateway NACKs ``*#5*0##``: the OWNd profile flag first, then the model table. | ||
|
|
||
| ``None`` on the profile means unverified and falls through to the table; a gateway | ||
| in neither is polled as before. | ||
| """ | ||
| profile = getattr(self._gateway_handler, "profile", None) | ||
| flag = getattr(profile, "supports_alarm_status_request", None) | ||
| if isinstance(flag, bool): | ||
| return not flag | ||
| model = getattr(self._gateway_handler, "model", None) | ||
| return isinstance(model, str) and model.strip().upper().replace(" ", "") in ALARM_STATUS_REFUSED_MODELS | ||
|
|
||
| async def async_update(self) -> None: | ||
| """Request status from the gateway.""" | ||
| """Request status from the gateway, unless the gateway refuses the request.""" | ||
| if self._status_request_refused: | ||
| LOGGER.debug( | ||
| "%s %s: %s refuses *#5*0##; waiting for alarm frames on the bus", | ||
| self._gateway_handler.log_id, | ||
| self._display_name, | ||
| self._gateway_handler.model, | ||
| ) | ||
| return | ||
| await self._gateway_handler.send_status_request(OWNAlarmCommand.status(self._where)) |
There was a problem hiding this comment.
Three issues in _status_request_refused / async_update:
- Shared-bus delegation & warm-standby failover bypassed:
MyHOMEGatewayHandler.send_status_request(cmd)(gateway.py:638-644) routes through_delegated_target(cmd)and_failover_target(cmd), whereas_status_request_refusedinspectsself._gateway_handlerdirectly. On a shared-bus plant such asMyHomeServer1primary +MH202secondary carryingWHO 5(#564/#576), an alarm entity registered on the primary (myhome.yamlor pre-delegation entity registry) hasself._gateway_handler=MyHomeServer1and skips*#5*0##even thoughsend_status_requestwould send it through the connectedMH202secondary that does answer*#5*0##. Please resolve the effective target gateway (target = gw._delegated_target(cmd) or gw._failover_target(cmd) or gw, cf.MyHOMEExposeManager._effective_profileinexpose.py:1042-1051) and checktarget'sprofile/model. - SKU / alias normalization:
gateway.modelreturns rawCONF_NAME, somodel.strip().upper().replace(" ", "")misses hyphens and SKU/short aliases normalized byOWNd.profiles.get_gateway_profile()(e.g."573992"→"F453AV","MHS1"→"MyHomeServer1"). Prefergetattr(profile, "model_name", None)(whenprofileis notGenericGatewayProfile) before falling back totarget.model. - Log message frame & model: Line 211 hardcodes
*#5*0##andself._gateway_handler.modeleven whenself._where != "0"(e.g. a manualmyhome.yamlzone entry sending*#5*#1##) or when routed totarget. Logcmdandtarget.modelinstead.
| if live_state is not None: | ||
| self._attr_alarm_state = live_state | ||
| # Only a frame that sets the state confirms it; activation or zone frames do not. | ||
| self._attr_extra_state_attributes.pop("restored", None) | ||
|
|
||
| self._attr_extra_state_attributes["raw_state"] = message.state_name | ||
| self._attr_extra_state_attributes["state_code"] = message.state_code |
There was a problem hiding this comment.
When live_state is None (*5*1*0## activation, *5*11*#1## active zone, battery/mains events) while "restored" is still True, unconditionally overwriting raw_state and state_code leaves mismatched attributes (e.g. alarm_state: armed_away, restored: true, raw_state: "activation", state_code: 1), which would also be persisted across a second restart before confirmation. Consider either keeping the restored raw_state/state_code until live_state is not None or tracking bus telemetry separately.
| * A new panel is **unknown** until the central unit reports a state. After a restart it shows its last known state with the attribute `restored: true` until the next frame confirms it. | ||
| * F450, F453AV, F454, F459, F460, F461 and MyHomeServer1 refuse the status request `*#5*0##`, so the panel is not polled there and waits for the central unit's own frames. |
There was a problem hiding this comment.
- Contradiction with line 16 above: Line 16 in this same file states "An F454 answers with system-level frames that have an empty WHERE (
*5*1*##,*5*5*##,*5*7*##,*5*9*##, see Need help configuring Alarm feature (F454) #311)", whereas line 47 here says F454 refuses*#5*0##. Please reconcile with the#311F454 trace finding. - Template alarm
availabilityrecipe below (lines 67–69): The comment# Unavailable until the MyHOME panel has reported(availability: "{{ states('alarm_control_panel.alarm_0') in ['disarmed', 'armed_away', 'triggered'] }}") only holds on a blank first start now — after a restart,RestoreEntityrestoresalarm_0immediately todisarmed/armed_away/triggered, making the template panel immediately available on the unconfirmed restored state. Please update that comment (and optionally noteand not state_attr('alarm_control_panel.alarm_0', 'restored')for users who want the template panel unavailable until live bus confirmation).
|
|
||
| - **`myhome.send_message` and `myhome.sync_time` reject unknown keys.** A misspelt key such as `gw_mac` or a missing `message` raises a validation error instead of sending the frame to the default gateway; both services get text selectors in the visual editor (#189). | ||
| - **Climate zones show the setpoint in force (dimension 12).** When the thermostat regulates to another setpoint than the commanded one (dimension 14; 23.5 in force while 21.0 commanded on an F461), the entity and `local_target_temperature` now show the one in force instead of the commanded value plus the knob offset. Known limit: once a zone has reported dimension 12, a dimension 14 that is not the echo of a Home Assistant write does not move the slider until the next dimension 12 report or poll (see Known Limitations). | ||
| - **The burglar alarm panel starts `unknown` instead of `disarmed`.** It restores its last state after a restart (attribute `restored: true` until the central unit confirms it) and no longer polls `*#5*0##` on gateways whose firmware NACKs it (F450, F453AV, F454, F459, F460, F461, MyHomeServer1) (#177). |
There was a problem hiding this comment.
Please update the gateway list (F454) and remove/fix the (#177) citation here once the review points on alarm_control_panel.py are addressed.
…*0## where the firmware refuses it The panel claimed `disarmed` from creation and skipped the RestoreEntity path, so an armed system showed disarmed after every restart. No issue reports it; the motivation is the live F461 observation of the panel's default state. Start with alarm_state None, restore the last valid state flagged `restored: true` until a frame that names a state clears it, and do not poll `*michnovka#5*0##` on gateways whose firmware NACKs it (own-firmware-oracle trace-corpus rows). The gate reads an OWNd profile flag first and falls back to a model table; the flag belongs in OWNd. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…teway refuses it The model table ALARM_STATUS_REFUSED_MODELS turned emulator rows into a model-capability fact, and the physical traces committed in tests/fixtures disprove it: the OpenWebNet-HA#311 F454 (firmware 2.0) answers `*michnovka#5*#1##` with `*5*11*#1##` and reports `*5*1*##`, `*5*5*##`, `*5*7*##`, `*5*9*##`; the OpenWebNet-HA#629 F454 (1.0.34) and OpenWebNet-HA#619 F453AV (3.0.14) sweeps answer `*michnovka#5*0##` with `*5*0*##` / `*5*1*##`, `*5*9*##` and the zone frames. The own-firmware-oracle `nack` / `silent` rows (F450, F453AV, F454, F459, F460, F461, MyHomeServer1) are factory-blank emulations (tools/expected_divergences.yaml lists the F454 case under silent_bus_vs_populated_trace), and the live F461 2.0.11 and MyHomeServer1 3.87.13 NACKs (OpenWebNet-HA#799) were taken on plants without a central unit: they prove those plants NACKed, not that every plant with that gateway cannot answer. Silence is never converted into refusal. The table is removed. Only an explicit `supports_alarm_status_request = False` on the OWNd profile skips the poll; `None` or no flag polls. The flag belongs in OWNd (OpenWebNet-HA#381 pattern). A per-plant cache of a live NACK is not implemented here: the command session does not report a NACK back to the entity (the future resolves to the write time), so it would need gateway-session plumbing first. Tests: every formerly listed model with no flag is polled, explicit False skips, True / None poll; the three physical F454 / F453AV fixtures prove the request is answered and that `*5*9*##` confirms the restored state; the two live NACK traces prove nothing reaches the panel and the restored state is left alone. docs/configuration/alarm.md no longer contradicts its own point 1, and the release note describes the policy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ies the request `send_status_request()` routes through `_delegated_target(cmd)` and `_failover_target(cmd)`, but the skip looked at the entity's own gateway. On a shared bus such as a MyHomeServer1 primary with an MH202 secondary carrying WHO 5 (OpenWebNet-HA#564 / OpenWebNet-HA#576), the panel registered on the primary would have skipped a poll the secondary answers; in warm standby the disconnected primary decided instead of the standby that sends. Resolve the target the same way `send_status_request` does (delegated secondary, then standby, then the gateway itself, cf. MyHOMEExposeManager._effective_profile) and read that target's profile. The DEBUG line names the frame actually skipped (`*michnovka#5*#1##` for a zone entry, not a hard-coded `*michnovka#5*0##`) and the target's canonical `profile.model_name` (`MHS1` -> `MyHomeServer1`, `573992` -> `F453AV`), falling back to the configured name only for a Generic profile. Tests: delegation in both directions, failover in both directions, an explicit True polls whatever the configured alias, SKU, case or spacing, and the log label for a short alias, a SKU and a Generic profile. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…il a verdict `AlarmControlPanelState(last_state.state)` accepted every core value, so a stale `armed_home` (removed in OpenWebNet-HA#609) or a transient `arming` / `pending` from .storage/core.restore_state would have been restored and left the documented template panel unavailable. Restore only `disarmed`, `armed_away` and `triggered`; anything else starts unknown. While `restored` is set, frames that name no state (`*5*1*0##` activation, `*5*11*#n##` / `*5*18*#n##` zones, `*5*5*##` battery, `*5*7*##` mains) no longer overwrite `raw_state` / `state_code`, which would have persisted a mismatched `armed_away` + `activation` pair across a second restart. A verdict frame overwrites both and clears the flag; once live, telemetry updates them as before. Tests: each restorable state, six stale or transient states, five non-verdict frames followed by a verdict. The doc lists the frames that confirm a restored state. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The `availability` comment said the template panel is unavailable until the MyHOME panel has reported. With RestoreEntity that holds only on a blank first start: after a restart `alarm_0` is available at once on its restored state (`restored: true`) until the central unit confirms it. Say so, and give the `and not state_attr(..., 'restored')` clause for users who want the template panel unavailable until the bus has confirmed the state. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
OpenWebNet-HA#177 is a pre-v2 feature request about adding an F4201 central unit on a MyHomeServer1, before WHO 5 existed; it names no F454 and contains no sentence about the state after a restart. No open or closed issue describes the disarmed-after-restart behaviour; the motivation is the live F461 observation of the panel's default state. Remove the citation from the code comment and the test comments (the release note and the commit messages no longer carry it either). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
31d44a9 to
3b6b1bc
Compare
|
All five points are on the branch (head 3b6b1bc, history rewritten for the commit messages, rebased on the current base, still draft). While checking the F454 evidence I found two more physical contradictions in the corpus, so the fix went further than removing one model.
A second-opinion review of the diff added three points that are in as well (route resolved once; only real gateway handlers accepted as a route result; two overclaiming sentences in the docs reworded). Suite 4554 passed, 100 % coverage, all repo checks green; |
|
@TheDarkWizard would you please be so kind to check if this is still an issue in v2? or that these only occur in the v0.9.x. versions |
Are you sure this tag is for me? As you know, I have a BT4200 and not an alarm that responds to WHO=5. |
|
All review items are addressed and CI is green on 3b6b1bc, so this is out of draft for your re-review (the branch stays open to the outcome of your question to the other reporter: if the symptom does not exist on v2, closing it is fine by me). |
Yes that is probably the whole issue there is probably nobody with a who 5 alarm. Because all the alarms use the protocol of Ksenia @gdluck I don't think we have traces of anyone operating a connected who5 alarm. I can be wrong of course |
|
You are right, and sorry for the noise: this came out of a review of every open and closed MyHOME issue where the only evidence I had for the alarm behaviour was the emulated firmware, and nobody has a trace of a connected WHO 5 alarm on v2 (the F461 and MyHomeServer1 plants I used have none either). A fix without a plant that shows the problem has no place here. Closing it and dropping the branch; if a WHO 5 trace ever appears, the unknown-start and restore idea can be reconsidered against it. |
Requested by A
What is the problem
The burglar alarm panel is created as
disarmed(_attr_alarm_state = STATE_DISARMED,raw_state: "disarmed") before any frame from the central unit has been seen, and it overridesasync_added_to_hasswithout callingsuper(), so theRestoreEntitypath ofMyHOMEEntitynever runs. After every restart the panel claims "disarmed" for an armed system until the next arm/disarm frame. No open or closed issue describes this; the motivation is the live F461 observation of the panel's default state while reviewing the entity. On top of thatasync_updatepolls*#5*0##through the entity's own gateway even when a shared-bus secondary or a warm standby is the one that carries WHO 5.Related: #103, #151 (the WHO 5 panel requests this entity answers), #311, #619, #629 (physical F454 / F453AV captures with a central unit), #381 (profile-flag pattern), #564 / #576 (shared-bus delegation), #609 (
armed_homeremoved), #766 (how anackrow is read on the F460 / F461 / MyHomeServer1 class), #799 (merged: the live F461 / MyHomeServer1 traces).What is the solution
alarm_control_panel.py:alarm_state = None(HA showsunknown) andraw_state: None; a state only comes from a frame that names one (*5*2*0##/*5*9*0##/*5*0*0##disarmed,*5*8*0##armed, an alarm WHAT → triggered) or from the restored state.async_added_to_hassoverride is removed, so the base-class restore and deferred poll run.async_restore_last_staterestores onlydisarmed,armed_awayortriggeredwith the attributerestored: true(plus the storedraw_state/state_code); a stalearmed_home(removed in feat(alarm): read-only alarm panel; arm and disarm through AUX frames (#564) #609),arming,pending,unknownorunavailableis not restored.restoredis cleared only by a frame that sets the state; while it is set, activation (*5*1*0##), zone, battery and mains frames leaveraw_state/state_codeuntouched, so a mismatched pair is never written or persisted.async_updateresolves the gateway thatsend_status_requestwould route through (_delegated_target(cmd), else_failover_target(cmd), else the entity's gateway, asMyHOMEExposeManager._effective_profiledoes) and sends through that target, so the route is resolved once. The poll is skipped (DEBUG, naming the frame actually skipped and the target's canonicalprofile.model_name) only when that target's OWNd profile statessupports_alarm_status_request = False;Noneor no flag polls. There is no model table: see the oracle section for why. The flag belongs in OWNd (fix(gateway): close idle command sessions before the gateway drops them #381 pattern) and is proposed, not opened.docs/configuration/alarm.md: the unknown / restored behaviour, which frames confirm a restored state, the poll policy with the physical F454 / F453AV evidence, and the template-alarmavailabilitycomment (with the optionaland not state_attr('alarm_control_panel.alarm_0', 'restored')clause).tests/test_alarm_restore_unknown.py(new, 46 cases) and the two updated assertions intests/test_component_alarm.py..github/release-notes/2.0.0b16.md: one line under "Other behaviour changes".Companion PR: none in OWNd yet; the
supports_alarm_status_requestprofile flag this PR reads is proposed, not opened (#381 pattern).Review follow-up (review 5639821916)
tests/fixtures/plants/issue_311_f454/diagnostic_summary.json(firmware 2.0, lines 58–137:*#5*#1##→*5*11*#1##, startup*5*1*##,*5*5*##,*5*7*##,*5*9*##), the Central unit (WHERE#0) is marked "unresponsive" after two restarts, although it answers a plain status request #629 F454 1.0.34 sweep (*#5*0##→*5*0*##,*5*9*##,*5*5*##,*5*7*##,*5*11*#1..8##) and the Investigate lighting WHAT 19 status frames from BTicino F414 modular dimmers #619 F453AV 3.0.14 sweep (*#5*0##→*5*1*##,*5*9*##,*5*5*##,*5*7*##, zones) all answer on the bus, so F453AV had to go too. Checkedtests/fixturesfor the other listed models: there is no alarm capture for F450, F459 or F460, and the only F461 (2.0.11) and MyHomeServer1 (3.87.13) WHO 5 captures are the test(trace): ingest live F461 2.0.11 2026-10-23 capture and MyHomeServer1 3.87.13 reads behind OWNd #136-#142 and MyHOME #791-#798 #799 NACKs on plants without a central unit. A second reviewer's point was adopted: an emulatednack/silentrow is not a model-capability fact, and a NACK on a plant without a central unit proves only that this plant NACKed; so the skip is now flag-only (explicitFalseon the OWNd profile),Nonepolls, and silence is never converted into refusal. A per-plant cache of a real live NACK was considered and not implemented: the command session does not report a NACK back to the entity (the future resolves to the write time), so it needs gateway-session plumbing first.docs/configuration/alarm.mdline 16 and the edited section agree now; the release note names no gateway as refusing._status_request_refused: the effective target is resolved likesend_status_request(delegated secondary, warm standby, own gateway) and the request is sent through it; the decision reads that target's profile; the log namescmd(*#5*#1##for a zone entry) and the target's canonicalprofile.model_name(MHS1→MyHomeServer1,573992→F453AV), the configured name only for a Generic profile. Tests: MyHomeServer1 primary (profile refusing) + MH202 secondary carrying WHO 5 → the secondary is asked; the reverse → nothing sent; failover both ways; an explicitTruepolls whatever the configured alias, SKU, case or spacing; the log label for a short alias, a SKU and a Generic profile; a route result that is not a gateway handler falls back to the entity's gateway.raw_state/state_codeare kept whilerestoredis set and a non-verdict frame arrives (activation,*5*11*#1##,*5*18*#2##, battery, mains); restoration is restricted to{disarmed, armed_away, triggered}.availabilitycomment: rewritten; the optionalrestoredclause is given.gh issue list --search "alarm disarmed restart"returns Review follow-up for #609: how each alarm review comment was handled #615 / feat(alarm): read-only alarm panel; arm and disarm through AUX frames (#564) #609 review threads and unrelated restart issues); the body says so.#799is cited as merged, and theSkipping WHO=5 discovery: not supported by F461 profileline is attributed to_discovery_frames()+ OWNdDEFAULT_SUPPORTED_WHOalready on the base.State Diagram
stateDiagram-v2 direction TB state "1. Panel state" as Panel { [*] --> Unknown : entity created , before - disarmed from creation Unknown --> Restored : RestoreEntity, last disarmed / armed_away / triggered, attribute restored true Unknown --> Unknown : stored armed_home, arming, pending, unknown or unavailable - not restored Unknown --> Disarmed : *5*2*0## , *5*9*0## , *5*0*0## Unknown --> ArmedAway : *5*8*0## Unknown --> Triggered : alarm WHAT , is_alarm Restored --> Disarmed : *5*2*0## , *5*9*## , restored cleared, raw_state updated Restored --> ArmedAway : *5*8*0## , restored cleared Restored --> Triggered : alarm WHAT , restored cleared Restored --> Restored : *5*1*0## activation, zone, battery or mains frame - state and raw_state untouched Restored --> Restored : *#5*0## NACKed by the gateway - nothing reaches the panel Disarmed --> ArmedAway : *5*8*0## ArmedAway --> Disarmed : *5*2*0## ArmedAway --> Triggered : alarm WHAT } state "2. async_update poll" as Poll { [*] --> Target Target --> Decide : delegated secondary for WHO 5, else warm standby while the primary is down, else own gateway Decide --> Sent : profile.supports_alarm_status_request True or None, or no flag - *#5*WHERE## through the target Decide --> Skipped : profile.supports_alarm_status_request False - DEBUG with the frame and the target's canonical model }Hardware Verification
*#5*#1##answered by*5*11*#1##61 ms later; the central unit reports*5*1*##,*5*5*##,*5*7*##,*5*9*##, zones and*5*15*#3##at startup, and*5*1*0##→*5*8*0##/*5*2*0##→*5*9*0##on arm / disarm.#0) is marked "unresponsive" after two restarts, although it answers a plain status request #629 sweep, 2026-10-05) and F453AV 3.0.14 (Investigate lighting WHAT 19 status frames from BTicino F414 modular dimmers #619 sweep, 2026-10-04):*#5*0##answered with*5*0*##/*5*1*##,*5*9*##,*5*5*##,*5*7*##and the zone frames.*#13**16*3*87*13##, WHO 13 code 200, WHO 1013 model 67; read-only probe on 2026-10-24, no command sent to that plant):*#5*0##NACKed in 5 ms. No WHO 5 plant behind it.*#5*0##and*#5*#1##NACKed in 3 ms. Per test(oracle): read empty-bus NACKs on F460/F461/MyHomeServer1 by verdict #766 anackon the F460 / F461 / MyHomeServer1 class is the gateway's own refusal, not a bus reply; this plant has no central unit, so it proves that this plant NACKed, not what an F461 with a central unit does.unknown/ restore path was not observable. Four startups sent no*#5*0##; that absence belongs to the startup gate of_discovery_frames()+ OWNdDEFAULT_SUPPORTED_WHOalready onv2-phase1-architecture(Skipping WHO=5 discovery: not supported by F461 profile), not to this PR.How is it tested
tests/test_alarm_restore_unknown.py(new, 46 cases) and the two updated assertions intests/test_component_alarm.py.alarm_control_panel.pyfrom the base): 40 fail (38 in the new file, the 2 updated assertions), 17 pass; all pass after. Per review item, on the commit before each: item 1 (flag-only poll + replay tests) 13 fail, item 2 (target / label) 6 fail, item 3 (restore / raw_state guard) 9 fail.master(f04d8aa, the behaviour of the 2.0.0b11 pin), rebased tree onv2-phase1-architecture31ea9fd: 4554 passed, 1 skipped (the base merge 31ea9fd alone is not re-run here; the previous base d8bac15 was 4418 passed, 1 skipped).scripts/verify_ownd_coverage.py100 % on all 73 modules,scripts/typing_ratchet.py0 errors (baseline 0),ruff check .,scripts/verify_ha_standards.py,scripts/sync_documentation.py --check,scripts/manage_translations.py check,tests/test_fixture_privacy.pyall pass.Live retest (2026-10-24)
Consensus label: entity path not exercised (no alarm panel on the plant; the absence of
*#5*0##at startup belongs to the base's discovery gate).Where are the traces
tests/fixtures/plants/issue_311_f454/diagnostic_summary.json(#311)*#5*#1##→*5*11*#1##; startup*5*1*##,*5*5*##,*5*7*##,*5*9*##;*5*8*0##,*5*2*0##,*5*9*0##(replayed:*5*9*##confirms the restored state)tests/fixtures/traces/issue_629/myhome_sweep_F454_all_2026-10-05T10-51-55.json*#5*0##→*5*0*##,*5*9*##,*5*5*##,*5*7*##,*5*11*#1..8##(replayed)tests/fixtures/traces/issue_619/myhome_sweep_F453AV_all_2026-10-04T19-45-11.json*#5*0##→*5*1*##,*5*9*##,*5*5*##,*5*7*##,*5*18*#n##,*5*11*#2##(replayed)tests/fixtures/traces/f461_live_2026-10-23/runrun1_oracle_replay_readonly, steporacle_who5(#799, merged)*#5*0##,*#5*#1##→ NACK after 3 ms (note: who5_refused_locally); replayed: nothing reaches the panel, the restored state is left alonetests/fixtures/traces/myhomeserver1_live_2026-10-24/runrun2_reads_thermo_sound_alarm_lights, stepread_who5(#799, merged)*#5*0##→ NACK after 5 ms; replayed as abovetests/fixtures/traces/issue_466/(upstream, MH200N)*5*0*##,*5*9*##,*5*5*##,*5*7*##,*5*11*#N##, the same frames the oracleack / outrows show*5*1*0##, zone, battery, mains and verdict frames throughOWNEvent.parseWhat does the Firmware Oracle / Encyclopedia say
Published rows (
results/<P>/<V>/oracle/full/trace-corpus.tsv, index 53f9514),*#5*0##:nack/silentack/out*5*0*##,*5*9*##,*5*5*##,*5*7*##,*5*11*#1##…*5*11*#8##ack/outThese rows are documented as compatibility, not used as a gate. The emulations are factory-blank images without a central unit;
tools/expected_divergences.yamlalready records the F454 case (020051vs field1.0.34/2.0/2.0.48,*#5*0##and*#5*#1##undersilent_bus_vs_populated_trace), and the physical F454 and F453AV captures above answer the request their emulations NACK. The live F461 2.0.11 and MyHomeServer1 3.87.13 NACKs agree with their emulated rows but were taken on plants without a central unit, so they do not say what a plant with one does. Whether the request is answered cannot be inferred from the gateway model alone; the gate is therefore the OWNd profile flag only, and the oracle rows stay here as the record of what the emulations do.Checklist
What is the problem,What is the solution,State Diagram,How is it tested,Where are the traces,What does the Firmware Oracle / Encyclopedia say).#0) is marked "unresponsive" after two restarts, although it answers a plain status request #629) and the live F461 / MyHomeServer1 NACKs in test(trace): ingest live F461 2.0.11 2026-10-23 capture and MyHomeServer1 3.87.13 reads behind OWNd #136-#142 and MyHOME #791-#798 #799; the unknown / restore change is state handling with no new frames.handle_event(); noasync_dispatcher_connect.pytest,ruff, andscripts/verify_ha_standards.pypass.🤖 Generated with Claude Code