✨ Device-scoped logbook events (zha_event, pings) in the activity/device log - #20
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Home Assistant's own device page shows more than state changes — a Zigbee
button press, a
zha_event, a device ping. hatty's activity/device log onlyever showed entity state changes, because its REST
/api/logbookfetch hasno device filter at all: HA's REST logbook view accepts only
period,entity,end_time,context_id. Device-scoped events carry adevice_idand no
entity_id, so they can never match anentity=filter. Only thewebsocket commands (
logbook/get_events,logbook/event_stream) acceptdevice_ids— which is exactly what HA's frontend device page passesalongside the device's entities.
Getting there also exposed that
HAClienthad no way toawaita websocketcommand's own response — every WS request was fire-and-forget, demuxed later
by label. That's issue #16, the enabler underneath everything else here.
HAClient(_request),alongside the existing fire-and-forget
_send.fetch_logbooknow trieslogbook/get_eventsover WS first(carrying
device_ids), falling back to the REST endpoint when thewebsocket is unavailable, latching off on old-HA "unknown_command"/
"invalid_format" responses. A new pure
hatty/logbook.pynormalizes the twotransports' different entry shapes (WS: epoch
when, nonameon stateentries) into one
LogEntry, rendered with a⚡prefix for device events.--demonow includes a Zigbee button and a door sensor with fake deviceevents so the feature is visible offline.
A(device log) now cycles: first press covers every devicebehind the active list (or the graphed entity's device), a second press
narrows to the device under the cursor, a third closes.
a/istayentity-only, matching HA. The fullscreen graph gained its own
Atoo, withdevice-scoped events drawn as distinct cyan marks vs. magenta state marks.
logbook/event_stream(state_changed can never carry them), with a dedupe guard for the
fetch/stream boundary overlap and resubscription on reconnect.
Behavior changes to flag for review
Ano longer always closes on the second press. With a list active, itnarrows to the cursor's device first; only the third press closes. No
change when a graph is open or no list is active (there's only ever one
device to show in those cases).
state_changedlive-append is suppressed while alogbook/event_streamsubscription is active (i.e., whenever a live logis open), since the stream already carries the same state changes plus the
device events
state_changedcan never see.Test plan (#16-19)
pytest— 1161 passeduv run pyright— 0 errorsuv run ruff check .— cleanuv run hatty --demo— cursor on the Zigbee button'sbattery entity,
Ashows⚡ Living Room Button: …lines; pressingAagain narrows/closes as expected;
GthenAon the graph shows cyanevent marks.
per
CLAUDE.md); worth verifying against a live Zigbee device and,ideally, an older HA instance to exercise the REST fallback.
Follow-up: activity log labels, maximize, and log-view cycle
Three more issues in the same activity-log area, bundled together since they
touch the same log-panel code:
Open/Closedfor adoorbinary_sensor) instead of rawon/off,matching how Home Assistant itself renders binary_sensor states. Fixed on
both the normalized fetch path and the legacy live
state_changedappend,so the same event can't read differently depending on transport (the
legacy
add_entrymethod was deleted in favor of routing through theshared
normalize_entry).f) now actually re-truncatesalready-written lines to the new width. Two bugs fixed: the panel now
retains its entries and reflows them on resize instead of baking
truncation width into each line at write time, and the width source itself
was wrong (
content_size.widthdoesn't account forLog's always-onvertical scrollbar;
scrollable_content_region.widthdoes). The fullscreengraph screen also gained its own
fmaximize, which it didn't have before.A(device-scoped log) isreplaced by
v, which cycles through three widening views: plottedentities only → + their devices' events → + every sibling entity on those
devices. Sibling entities' state changes (only visible in the widest view)
are now marked orange on the plot rather than magenta, since magenta
previously implied a change on a plotted line.
#23 (select a log line, view full text in a popup) was scoped out during
planning — it requires reversing a deliberately documented
can_focus=Falseinvariant and re-solving key routing across five bindings, and is a better
fit for its own PR now that #22 has already solved its main prerequisite
(untruncated entry retention).
Test plan (#21, #22, #25)
uv run ruff check .— cleanuv run pyright— 0 errorspytest— full suite, 1186 passeduv run hatty --demo: door sensor reads Open/Closed;fon both the main table and fullscreen graph re-expands/re-truncateslong lines;
G→a→vvvcycles Activity Log → Device Log →Device Entities Log → back, with orange marks only in the widest view
Closes #16, closes #17, closes #18, closes #19, closes #25, closes #22, closes #21
🤖 Generated with Claude Code
https://claude.ai/code/session_01KSut7AwCYk11MxgbVaxwc9