Repository navigation
feat(crow_alarm_panel): decode RF remote events (0x7C), protocol doc updates - #16
Conversation
Frigate window 2026-09-05 20:47 -> 2026-09-07 05:14 UTC: every clustered ff./fe. burst still precedes a watchdog trip (5/5 across two windows), one burst now cost two consecutive trips. Bigger finding: an 8h36m-armed disarm retried with CURRENT_TIME confirmed normal throughout, mirroring the existing confirmed-stuck-but-instant case -- the CURRENT_TIME-stuck/retry correlation tracked since 2026-08-31 no longer looks predictive either way. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lf3cEpQ9z4E8bEeb95ovEy
Manual bit-level decode of the raw bit trace around the 2026-09-07 watchdog bursts shows the same two corrupted frames repeating each time, matching the already-documented controller-retransmission-storm mechanism, with the isolated single fe. line landing on a different keypad's slot as expected. A stateful ISR replica didn't reproduce the real logged counts, tracing to a previously undocumented limitation: the bit-trace buffer itself silently drops bits under load, unlike the (already-fixed) frame queue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lf3cEpQ9z4E8bEeb95ovEy
Frigate capture review for 2026-09-07 05:14 -> 2026-09-08 08:40 UTC. Poll-slot/watchdog correlation extends to 7/7 with no exceptions; the invalid-minutes-since-midnight fault recurred a 3rd day running at the same 11:25:55-11:27:40 UTC window, and the day/month=*/36 fault gained a new day value (32) extending the fixed +4 set.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PNDZYZ3LXccQmKrrBzX6CL
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PNDZYZ3LXccQmKrrBzX6CL
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PNDZYZ3LXccQmKrrBzX6CL
Give the RF remote's button-press frame (data[0..2] remote identity, data[3..4] button code) a proper log line instead of falling through to the generic Unknown handler. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DUsSRQuhQShyCnUNPsfqaQ
Two-remote scripted button test decodes all four RF remote functions (arm/disarm/gate/garage) against OUTPUT_STATE and confirms 0x7C causality precedes the controller's action by ~100-150ms. Cross-references the vendor ESL-2 install/programming manual: confirms the RF-remote (0x7C) attribution and per-button radio-user addressing, and resolves the Output-1 siren-chirp mechanism as the documented Pendant Arm/Disarm Chirp feature (P50E-P53E). Resolves the long-open "invalid day/month value */36" mystery from the frigate log-mining effort: it's real_calendar_value x 4, caught live transitioning across a local-midnight rollover, not a stuck or independently-incrementing value. Proposes then disproves a dialler-test-call hypothesis for the daily invalid-minutes-since-midnight glitch (wrong configured time, and the dialler isn't active on this installation) -- recorded so the explanation isn't re-proposed later. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DUsSRQuhQShyCnUNPsfqaQ
Frigate long-term log window 2026-09-10 04:55 -> 2026-09-13 04:14 UTC: first observed case of ff./fe. bus corruption disrupting an in-flight Output-select sequence (timeout/retry + recovery both fired, sequence still completed correctly), documented alongside a new known_quirks.md entry mirroring the existing arm/disarm-retry one. Also revises the malformed 7-byte ARMED_STATE decode from "disarm-after-arm-specific" to "rare receiver-side glitch" after a second occurrence with no adjacent arm/disarm activity. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqNbZBEyPpzMhXAB5wk2Y6
Frigate long-term log window 2026-09-13 04:14 -> 2026-09-14 07:26 UTC: ff./fe. corruption and watchdog trips both zero for the full ~27h span, and the one integration disarm retry this window (9h31m armed, normal CURRENT_TIME) had no bus corruption anywhere nearby -- the cleanest counter-example yet against the corruption-proximity retry theory. Also logs a 3rd malformed ARMED_STATE decode with a new type-byte variant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014n8DhRT3HAsqKrNkb11ETi
There was a problem hiding this comment.
🟡 Changes recommended
The RF parser accepts truncated frames as valid button events, and documentation corrections remain outstanding.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds passive, log-only decoding for RF remote 0x7C events and expands protocol documentation with trace findings and user-facing quirks.
Changes:
- Adds RF event recognition, identity, and button logging.
- Documents packet layouts, causality, corruption, retries, and state-machine findings.
- Adds and links a known-quirks guide.
File summaries
| File | Summary | Review notes |
|---|---|---|
components/crow_alarm_panel/README.md |
Links the known-quirks guide. | No findings. |
components/crow_alarm_panel/docs/protocol_wire_format.md |
Documents RF packet details and causality. | Nits: correct the receiver-to-controller direction and qualify timing measurements, including the same-millisecond exception. |
components/crow_alarm_panel/docs/protocol_investigations.md |
Adds trace-derived protocol findings. | Nits: correct the event count and qualify the observed timing range. |
components/crow_alarm_panel/docs/known_quirks.md |
Adds user-facing troubleshooting guidance. | Nits: correct log-level wording, stale watchdog/corruption conclusions, grammar, and CURRENT_TIME recovery behavior. |
components/crow_alarm_panel/docs/arm_disarm_state_machine.md |
Updates retry investigation history. | No findings. |
components/crow_alarm_panel/crow_alarm_panel.h |
Defines the RF event constant. | No findings. |
components/crow_alarm_panel/crow_alarm_panel.cpp |
Decodes and logs RF remote events. | Moderate: truncated 3–7 byte frames can be logged as valid button events instead of being rejected. |
CLAUDE.md |
Documents known-quirks maintenance guidance. | No findings. |
Review details
Suppressed comments (6)
components/crow_alarm_panel/docs/known_quirks.md:31
- This summary is stale relative to the new 2026-09-14 investigation entry: a retry occurred during a full 27-hour window with zero
ff./fe.corruption or watchdog activity, so corruption/watchdog proximity is explicitly no longer the current lead. Describing it as the current lead will send users toward a hypothesis the updated evidence has undermined; update this sentence to reflect that the cause remains open and lower priority.
cause has been confirmed — a `CURRENT_TIME` correlation theory was tested and later ruled
out; a bus-corruption/watchdog-proximity theory is the current (unconfirmed, single-data-point)
lead. See `arm_disarm_state_machine.md` for the full retry investigation history.
components/crow_alarm_panel/docs/known_quirks.md:86
- Use the article
anbefore theUnknownlog label.
exactly when a `Unknown [ff.]`/`[fe.]` corruption burst (see above) happens to land in
components/crow_alarm_panel/docs/known_quirks.md:111
- The implementation can recover the known doubled-date corruption when its weekday check succeeds (
crow_alarm_panel.cpp:604-618); it only discards unrecoverable values. Saying all corruptedCURRENT_TIMEframes are discarded is inaccurate and contradicts that recovery behavior. Please describe the validation/recovery path instead.
**What to do:** nothing — the component already discards corrupted `CURRENT_TIME` frames
rather than acting on them, and no entity depends on this data.
components/crow_alarm_panel/docs/protocol_investigations.md:865
- The conclusion here also generalizes the timing as roughly 100 ms for all eight combinations, but the table immediately above has one same-log-millisecond case and measured deltas from +98 to +147 ms. Keep the causal ordering claim, but qualify the delay to match the observed values and timestamp resolution.
**Inference (high confidence):** this directly answers the open question from the 2026-09-09 second-follow-up entry above ("not yet established whether `0x7C` and the siren chirp are cause-and-effect... or two independent effects") — `0x7C` is upstream: the RF receiver hardware reports the button press to the controller, which then acts on it and broadcasts the state change roughly 100ms later. Not a parallel/simultaneous echo of an action already decided elsewhere. Written into `protocol_wire_format.md`'s `0x7C` entry as a new "Causality" paragraph.
components/crow_alarm_panel/docs/protocol_wire_format.md:494
- This timing summary overstates the uniformity of the measurements: the detailed table reports deltas from +98 to +147 ms, while Remote A's arm case is only "same log ms, 0x7C logged first." Please describe the measured range and the same-millisecond/log-order exception instead of saying all eight were ~100–150 ms.
**Causality (high confidence, resolves the previously-open question):** the first `0x7C` frame of each pair precedes the controller's resulting action (the `ARMED_STATE`/`Disarmed` broadcast, or the `OUTPUT_STATE` pulse) by ~100–150ms in all 8 button presses checked (both remotes × all 4 buttons) — never simultaneous-or-after. `0x7C` is therefore the RF receiver reporting the button press *to* the controller, which then acts on it ~100ms later — a cause, not a parallel echo of an action already taken.
components/crow_alarm_panel/docs/protocol_wire_format.md:451
- The new causality section identifies the RF receiver as reporting the button press to the controller, but this field still says
Unknown source → broadcast. That is contradictory and leaves protocol implementors with the wrong direction; update it to the confirmed receiver-to-controller direction or explain why the direction remains unknown.
*Direction:* Unknown source → broadcast (not yet attributable to a keypad address; no `keypad_addr` byte pattern matches the existing keypad-address convention)
- Files reviewed: 8/8 changed files
- Comments generated: 4
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Reject RF remote frames shorter than 5 bytes (need data[3..4] for the button code) instead of the too-loose 3-byte minimum, which let truncated frames log as valid button events. Also fixes several doc inaccuracies the review caught: a stale "Unknown source" direction note contradicting the new causality finding, an overgeneralized ~100ms timing claim, a wrong button-count sum, and known_quirks.md claims that had gone stale relative to the 2026-09-14 zero-corruption retry counter-example and the CURRENT_TIME recovery logic. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The RF payload-length guard and several documentation details remain inconsistent with the documented behavior.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (7)
components/crow_alarm_panel/crow_alarm_panel.cpp:926
- This log labels the packet type (
type, always0x7c) as the button event, while the per-button code isdata[3..4]. As written, every button log has the same button label and the new decode does not expose which button code was received; logdata[3]anddata[4]explicitly.
ESP_LOGD(TAG, "[RF Remote %02x:%02x:%02x] Button event [%02x.%s]", data[0], data[1], data[2], type,
format_hex_pretty(data).c_str());
components/crow_alarm_panel/docs/arm_disarm_state_machine.md:605
- This says the bus cannot distinguish RF-remote input, but this PR adds exactly that distinction: the preceding 0x7C frame is now decoded and logged before the ARMED_STATE broadcast. Please scope the limitation to ARMED_STATE/keypress context alone so this historical comparison does not contradict the new protocol finding.
**Source:** the frigate long-term logger, window 2026-09-08 08:40 → 2026-09-09 05:09 UTC. Three arm/disarm sequences this window; the first two were confirmed by the user to be triggered by the official RF remote (distinct chime pattern: once on arm, twice on disarm) rather than a keypad or this integration — the bus itself can't distinguish remote-fob input from any other non-integration source, since both simply appear as Controller `ARMED_STATE` broadcasts with no accompanying keypress/command traffic.
components/crow_alarm_panel/docs/arm_disarm_state_machine.md:641
- This repeats the now-stale claim that the bus cannot identify a remote-fob event. With the new 0x7C decoder, the bus trace has a direct RF-remote signature; only an ARMED_STATE broadcast considered in isolation is ambiguous. Please update this note to reflect that distinction.
- Audible chime count on the official RF remote distinguishes it from other input sources when reading a trace: **one chime on arm, two chimes on disarm** (confirmed by the user, 2026-09-09). Useful because the bus itself can't tell remote-fob input apart from any other non-integration source — both simply appear as a Controller `ARMED_STATE` broadcast with no accompanying keypress/command traffic, unlike physical-keypad or integration-initiated sequences which leave a `Code sequence:`/keypress trail
components/crow_alarm_panel/docs/protocol_investigations.md:905
- The default 240-minute interval is exactly 6 × 240 = 1440 minutes, so it does divide evenly into a day. This incorrect arithmetic dismisses a potentially relevant periodic source; please correct the statement while keeping the phase/configuration uncertainty explicit.
**Follow-up (2026-09-10, same day): hypothesis disproven, and doubly so.** User checked `P175E4E` directly on the physical LCD keypad — the panel's configured Test Call Start Time is `02:00` (NZST), not anywhere near `23:25`–`23:28`. On top of that, the user believes the dialler itself (`P175E1E` Option 1, "Dialler is Enabled") is not active at all — no monitoring service/phone line in use on this installation — so even the `02:00` value is a vestigial default that never actually fires a call. This rules out the Automatic Test Call as the trigger for the daily `invalid minutes-since-midnight` episode on two independent grounds (wrong time, *and* the feature isn't even running). `02:00` NZST = `14:00` UTC doesn't line up with any other currently-documented daily-timed signature either (checked against this file and `arm_disarm_state_machine.md` — no match). The "what happens near `11:26` UTC daily" question reverts to fully unidentified, with the dialler-contention mechanism ruled out specifically — worth remembering not to re-propose that particular explanation, including any dialler-related mechanism generally (test calls, DTMF, modem tones), since the dialler isn't believed to be active on this panel at all. If a future session wants another candidate, look elsewhere in the panel's periodic/scheduled behaviors (e.g. the radio-detector supervised-timer check-in cycle, `P25E4E`, default 240 min — doesn't divide evenly into a once-daily period at the default, but worth a look if it's been reconfigured).
components/crow_alarm_panel/docs/protocol_investigations.md:853
- The source test reports 16 physical presses (8 per remote), while this heading and the next line say "all 8 presses." The table contains 8 remote/button combinations, so distinguish combinations from physical presses and state whether the ordering check covered one representative press per combination or all 16.
### Update (2026-09-10, cont.): `0x7C` precedes the controller's action by ~100–150ms in all 8 button presses — resolves the cause-vs-parallel-effect question left open on 2026-09-09
**Source:** user asked directly whether the `0x7C` frames arrive before the action they correlate with; checked sub-millisecond ordering for all 8 presses from the update above.
components/crow_alarm_panel/docs/protocol_wire_format.md:453
- This section says an RF event requires 8 payload bytes, but the handler accepts and logs any 5-byte payload because the first five bytes are the identity plus per-button code it decodes. Please distinguish the parser's minimum from the 8-byte shape observed in captures; otherwise the documentation and implementation disagree about which short frames are valid.
*Min length:* 8 payload bytes
components/crow_alarm_panel/docs/protocol_wire_format.md:494
- The scripted test above reports 16 physical presses (8 per remote), but this sentence calls the causality sample "all 8 button presses." The table appears to cover 8 remote/button combinations, so clarify whether one representative press per combination was checked or all 16 presses; otherwise the evidence sample size is ambiguous.
**Causality (high confidence, resolves the previously-open question):** the first `0x7C` frame of each pair precedes the controller's resulting action (the `ARMED_STATE`/`Disarmed` broadcast, or the `OUTPUT_STATE` pulse) in all 8 button presses checked (both remotes × all 4 buttons) — never simultaneous-or-after. Measured deltas range from +98ms to +147ms, with one exception (Remote A's arm case landed in the same logged millisecond, `0x7C` still ordered first in the log). `0x7C` is therefore the RF receiver reporting the button press *to* the controller, which then acts on it roughly 100ms later — a cause, not a parallel echo of an action already taken.
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Lite
| break; | ||
| } | ||
| case RF_REMOTE_EVENT: { | ||
| if (data.size() < 5) { |
Log the RF remote's actual button code (data[3..4]) instead of just the packet type, so distinct buttons no longer produce identical log lines. Reconcile the wire-format doc's "8 payload bytes" minimum with the parser's real 5-byte requirement. Fix a 240/1440-minute divisibility error in the P25E4E speculation. Clarify "8 button presses" as 8 button/remote combinations (one representative press each), not all 16 physical presses. Scope two historical "bus can't distinguish RF-remote input" notes in arm_disarm_state_machine.md to "ARMED_STATE considered in isolation," since this PR's 0x7C decode is now a direct signature. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Two unresolved documentation inconsistencies remain in the protocol investigation and wire-format references.
Review details
Suppressed comments (2)
components/crow_alarm_panel/docs/protocol_investigations.md:502
ARMED_STATEis0x11, and the C++ switch only emitsArmed state unknowninside that case. A frame rendered as[13.83...]has type0x13and would instead be logged by thedefaultbranch asUnknown [13...], so this new observation is inconsistent with the implementation and with the type-byte-corruption inference below. Please describe it as the malformed/unknown0x13frame and adjust the heading accordingly.
### Update (2026-09-14): `ff.`/`fe.` corruption and watchdog trips both drop to zero for a full ~27h window; third `ARMED_STATE` malformed-decode occurrence has a different type byte than the first two
**Source:** frigate long-term logger, window 2026-09-13 04:14 → 2026-09-14 07:26 UTC (~27h13m, no reboot/OTA in-window) ([[project-crow-alarm-protocol-trace]]).
**Findings (observed facts):** zero `Unknown [ff.]`/`[fe.]` lines anywhere in the entire window — a sharper version of the 2026-09-06 dip (2-in-21h) with no corruption at all this time, against an established baseline of ~0.84–0.94/h. Registration-watchdog trips: also zero, consistent with (not a counter-example to) the poll-slot/watchdog mechanism, since there was no corruption burst to trigger one. Frame-FIFO backlog/overflow counters stayed at zero throughout, and no truncated-frame WARNs (`Controller status too short`, `Zone state invalid length`, `Output state too short`, `Current time too short`) occurred. A third instance of the malformed 7-byte `Armed state unknown` decode appeared at `2026-09-13 15:40:25` UTC — `[13.83.01.46.81.00.80.11 (7)]`, identical to the previous two except the leading type byte is `0x13` rather than `0x11` (bytes 2–7 unchanged) — again with no adjacent arm/disarm activity, consistent with the 2026-09-13 revision to "rare receiver-side glitch, not context-gated."
components/crow_alarm_panel/docs/protocol_wire_format.md:435
- This wording overgeneralizes the observed September case: halving a
×4month gives2×month, which is out of the1–12range for months 7–12 but remains in range for January–June (with the weekday check then determining recovery). Please scope the claim to the observedmonth=36/September data or describe the full condition so the protocol reference does not imply every×4frame is rejected for this reason.
A related but distinct variant fails this recovery outright: `day`/`month` corrupted by
a `×4` (not `×2`) multiplier, which still halves to an even number but lands `month` out
of the valid `1–12` range, so the frame is discarded rather than silently mis-recovered.
- Files reviewed: 8/8 changed files
- Comments generated: 0 new
- Review effort level: Lite
The 2026-09-14 malformed-decode entry called a type-0x13 frame an "Armed state unknown" decode, but ARMED_STATE's switch case only matches 0x11 — a 0x13 frame actually falls through to the default branch and logs as "Unknown [13...]". Corrected the heading and body to describe it accurately. The CURRENT_TIME x4-corruption note claimed the day/month recovery logic always rejects x4-corrupted frames via the out-of-range check. That's only guaranteed for real_month >= 7 (all cases observed so far); for real_month <= 6 the single-halving recovery lands back in the valid 1-12 range and only the probabilistic weekday cross-check would catch it. Documented as a theoretical gap, not yet observed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Documentation claims remain inconsistent with the recorded evidence, including an overstated behavior frequency.
Review details
Suppressed comments (9)
components/crow_alarm_panel/docs/arm_disarm_state_machine.md:641
- The new summary omits the exception recorded in
protocol_wire_format.md: Remote A's arm event had the0x7Cframe ordered first in the same logged millisecond, not 100–150 ms earlier. Qualify this as the typical measured delay so readers do not treat that latency as guaranteed.
- Audible chime count on the official RF remote distinguishes it from other input sources when reading a trace: **one chime on arm, two chimes on disarm** (confirmed by the user, 2026-09-09). This was historically the only way to tell, since an `ARMED_STATE` broadcast considered in isolation looks the same as any other non-integration source — no accompanying keypress/command traffic, unlike physical-keypad or integration-initiated sequences which leave a `Code sequence:`/keypress trail. A direct on-bus signature now also exists: the `0x7C` RF-remote-event frame (see `protocol_wire_format.md`) precedes the resulting `ARMED_STATE` broadcast by ~100–150ms, so a trace no longer has to rely on the chime alone
components/crow_alarm_panel/docs/known_quirks.md:28
- This summary contradicts the retry history documented in the same PR: the 2026-08-19 entry records two disarm calls exhausting the then-five-retry budget and aborting. Please scope the claim to the post-six-retry-backoff period and update the “has not been observed” sentence below, otherwise users are told a failure has never occurred when it has.
controller doesn't confirm in time. This has always resolved successfully within the
6-retry budget in every case observed so far (worst case seen: ~42s). No root cause has
components/crow_alarm_panel/docs/known_quirks.md:77
Unknown [ff.]/[fe.]bursts are not always merely cosmetic: the entries immediately below document that the same burst can trigger this component's watchdog and delay an in-flight output sequence. Limit this statement to isolated lines and describe clustered bursts as self-recovering but potentially disruptive.
**What to do:** nothing — purely cosmetic log noise from bus-level behavior outside this
integration's control.
components/crow_alarm_panel/docs/known_quirks.md:55
- This attributes arm/disarm retries to the same corruption mechanism, but the linked arm/disarm investigation explicitly says corruption proximity was not established and has clean counterexamples. Keep the confirmed output-select mechanism here, but state that arm/disarm's cause remains open.
**Cause:** the same bus-corruption noise documented below (`Unknown [ff.]`/`Unknown [fe.]`)
can occasionally land in the slot where the controller's next confirmation was expected
during an output-select sequence, same as it can for arm/disarm. The state machine's
built-in timeout/retry and out-of-sequence recovery logic re-synchronizes and completes
components/crow_alarm_panel/docs/known_quirks.md:85
- The stated frequency is inconsistent with the evidence summarized here: the investigation records full ~21–27-hour windows with zero watchdog trips and rates around 0.05–0.2/hour, not a stable 'every few hours' cadence. Please describe this as intermittent with highly variable frequency rather than setting that expectation.
**What you'll see:** a `WARN`-level log line, roughly every few hours in long-term
captures, followed immediately by the integration re-registering itself on the bus. No
functional interruption — entities keep working normally.
components/crow_alarm_panel/docs/known_quirks.md:117
- The new
×4analysis explicitly identifies a theoretical gap: for a×4-corrupted date in real months 1–6, halving plus the weekday check can accept an in-range but wrong date by chance. This text currently presents the recovery/discard path without that caveat, which overstates its safety.
**What to do:** nothing — for day/month/year the component tries to recover the known
doubled-bit glitch first (cross-checked against the frame's own weekday field before
trusting the recovery) and only discards the frame if that check fails; other invalid
fields are discarded outright. Either way, no entity depends on this data.
components/crow_alarm_panel/docs/protocol_investigations.md:810
- These new 2026-09-09/10 RF entries are appended after
## Raw bit trace...but are formatted as### Update, so Markdown renders them as subsections of the raw-trace investigation; the later 2026-09-10 automatic-test-call entry is similarly grouped there despite being aCURRENT_TIMEinvestigation. Please restore topic-level##headings or place each update under its actual section.
### Update (2026-09-09, follow-up): the two remote-triggered sequences both carry a previously-unseen `Unknown [7c...]` packet pair, exclusively — new unlabeled type documented in `protocol_wire_format.md`
components/crow_alarm_panel/docs/protocol_wire_format.md:436
- This new
×4discussion relies on the “single-halving recovery above,” but the preceding text still says corrupted frames should be discarded rather than corrected even though the implementation deliberately recovers the known×2date glitch (crow_alarm_panel.cpp:604-618). Please distinguish known, range/weekday-validated recovery from unrecognized corruption so the protocol contract is not contradictory.
A related but distinct variant exists: `day`/`month` corrupted by a `×4` (not `×2`)
multiplier. Confirmed (2026-09-10) as `real_value × 4`, not arbitrary garbage — see
`protocol_investigations.md`'s 2026-09-10 update for the live midnight-crossing capture
that pins this down. Every occurrence observed so far has had `real_month ≥ 7`
components/crow_alarm_panel/docs/protocol_wire_format.md:458
Min lengthis used elsewhere in this document for the parser's minimum accepted payload, and the new parser accepts 5 bytes (data[0..4]), whereas this entry labels 8 observed bytes as the minimum. Please distinguish the parser minimum from the observed full-frame length to avoid misleading future implementations.
*Min length:* every capture observed is 8 payload bytes; the parser only requires 5 (`data[0..2]` identity + `data[3..4]` button code) since those are all it decodes
- Files reviewed: 8/8 changed files
- Comments generated: 0 new
- Review effort level: Lite
- known_quirks.md: scope the "always resolved within budget" retry claim to post-budget-increase behavior and mention the 2026-08-19 exhaustion under the old 5-retry budget that motivated the fix; stop implying arm/disarm retries share the output-select corruption mechanism (that's explicitly unconfirmed, unlike output-select's); acknowledge ff./fe. bursts can trigger downstream watchdog/retry symptoms rather than calling them purely cosmetic; replace the inaccurate "roughly every few hours" watchdog cadence with the actual highly-variable observed rates; note the x4 CURRENT_TIME recovery-gap caveat. - arm_disarm_state_machine.md: qualify the 0x7C causality delay as typical, not universal (one case was same-millisecond). - protocol_wire_format.md: stop saying corrupted CURRENT_TIME frames are discarded-not-corrected when the implementation deliberately recovers the known x2 glitch; reword the 0x7C Min length line to match the doc's own "N required (M seen in practice)" convention. - protocol_investigations.md: add two `##` topic headings so the newly-added RF-remote and CURRENT_TIME-periodicity entries stop rendering as subsections of the unrelated "Raw bit trace" section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
0x7Cas an RF remote button-press event (per-remote identity + per-button code, logged rather than acted on) and document its byte layout, causality, and vendor-manual cross-reference indocs/protocol_wire_format.md.docs/known_quirks.md: a user-facing translation of the reverse-engineering docs — recurring log WARNs and behaviors (retry sequences, corruption bursts, etc.) a user could actually notice, why they happen, and whether action is needed. Linked fromREADME.mdandCLAUDE.md.docs/protocol_investigations.mdanddocs/arm_disarm_state_machine.mdwith several trace-derived findings from the ongoing long-term capture: RF remote decode,CURRENT_TIME×4corruption-multiplier root cause, an output-select sequence recovering cleanly from mid-sequence bus corruption, and anARMED_STATEmalformed-decode revision.Test plan
uv run esphome config crow_alarm_panel_test.yaml(config validation)uv run esphome compile crow_alarm_panel_test.yaml(compile check)