Skip to content

✨ Device-scoped logbook events (zha_event, pings) in the activity/device log - #20

Merged
iTerminate merged 17 commits into
mainfrom
improvements
Jul 31, 2026
Merged

✨ Device-scoped logbook events (zha_event, pings) in the activity/device log#20
iTerminate merged 17 commits into
mainfrom
improvements

Conversation

@iTerminate

@iTerminate iTerminate commented Jul 28, 2026

Copy link
Copy Markdown
Owner

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 only
ever showed entity state changes, because its REST /api/logbook fetch has
no device filter at all: HA's REST logbook view accepts only period,
entity, end_time, context_id. Device-scoped events carry a device_id
and no entity_id, so they can never match an entity= filter. Only the
websocket commands (logbook/get_events, logbook/event_stream) accept
device_ids — which is exactly what HA's frontend device page passes
alongside the device's entities.

Getting there also exposed that HAClient had no way to await a websocket
command's own response — every WS request was fire-and-forget, demuxed later
by label. That's issue #16, the enabler underneath everything else here.

Behavior changes to flag for review

  • A no longer always closes on the second press. With a list active, it
    narrows 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).
  • The raw state_changed live-append is suppressed while a
    logbook/event_stream subscription is active
    (i.e., whenever a live log
    is open), since the stream already carries the same state changes plus the
    device events state_changed can never see.

Test plan (#16-19)

  • Full suite: pytest — 1161 passed
  • uv run pyright — 0 errors
  • uv run ruff check . — clean
  • Manual, offline: uv run hatty --demo — cursor on the Zigbee button's
    battery entity, A shows ⚡ Living Room Button: … lines; pressing A
    again narrows/closes as expected; G then A on the graph shows cyan
    event marks.
  • Manual, real HA — not run this session (requires explicit confirmation
    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:

  • Closes Activity log should use domain-aware state labels (e.g. open/closed for door sensors) #25 — the log now shows domain-aware labels (e.g.
    Open/Closed for a door binary_sensor) instead of raw on/off,
    matching how Home Assistant itself renders binary_sensor states. Fixed on
    both the normalized fetch path and the legacy live state_changed append,
    so the same event can't read differently depending on transport (the
    legacy add_entry method was deleted in favor of routing through the
    shared normalize_entry).
  • 🐛 Closes Fullscreen activity log still wraps/cuts text as if in a small window #22 — maximizing the log (f) now actually re-truncates
    already-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.width doesn't account for Log's always-on
    vertical scrollbar; scrollable_content_region.width does). The fullscreen
    graph screen also gained its own f maximize, which it didn't have before.
  • Closes Better shortcut for switching activity log views #21 — the fullscreen graph's A (device-scoped log) is
    replaced by v, which cycles through three widening views: plotted
    entities 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=False
invariant 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 . — clean
  • uv run pyright — 0 errors
  • pytest — full suite, 1186 passed
  • Manual smoke via uv run hatty --demo: door sensor reads Open/Closed;
    f on both the main table and fullscreen graph re-expands/re-truncates
    long lines; Gav v v cycles 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

@iTerminate
iTerminate merged commit ff8847e into main Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment