Describe the bug
After updating to 1.7.0, the calendar.llm_vision_timeline entity pins one CPU core at ~100% continuously and starves the Home Assistant asyncio event loop. HA logs, on repeat:
WARNING [homeassistant.helpers.entity] Update of calendar.llm_vision_timeline is taking over 10 seconds
WARNING [homeassistant.util.loop] Detected blocking call to open with args ('/usr/share/zoneinfo/Europe/Budapest', 'rb') inside the event loop
WARNING [custom_components.llmvision.timeline] [CLEANUP] Failed to remove /media/llmvision/snapshots/<uuid>-cam...
Because the event loop is saturated, unrelated integrations start timing out (in my case custom_components.frigate.api: Timeout error fetching ... — Frigate itself responds in <15 ms, the timeout is purely loop starvation).
I confirmed the culprit thread by sampling /proc/<pid>/task/*/stat from the host: the MainThread (event loop) is the consumer, not the recorder or executor. Disabling the calendar.llm_vision_timeline entity immediately drops the core to idle (MainThread 66% → 10%, load 1.2 → 0.4) and the Frigate timeouts stop. Re-enabling brings the pegged core back.
This is not a data-volume problem: my events.db is 1795 rows / 1.2 MB (~1 week of events). A reload over that should be milliseconds.
Root cause (code analysis, 1.7.0)
In calendar.py:
Calendar.async_added_to_hass() connects SIGNAL_TIMELINE_UPDATED to async_schedule_update_ha_state(force_refresh=True) — so every timeline write (i.e. every camera event) forces a full async_update.
Calendar.async_update() calls timeline.load_events() then get_all_events() and rebuilds all CalendarEvent objects from scratch each time.
Calendar.async_get_events() (called when the calendar card is viewed) also calls load_events() — a full reload per request.
In timeline.py:
load_events() (~L595) runs await self._purge_expired_events() and SELECT uid,title,start,end,... FROM events with no filter, then parses every row (dt_util.parse_datetime + _ensure_datetime) on every call.
_purge_expired_events() runs on every load_events() and appears to do blocking filesystem work on snapshots (hence the repeated [CLEANUP] Failed to remove ... warnings) plus per-event timezone conversions that trigger blocking zoneinfo file opens on the loop.
Net effect: O(n) DB reload + purge + blocking file/zoneinfo I/O on the event loop, executed on every event write and amplified by event frequency. Even at ~1.8k rows it exceeds the 10 s warning threshold and never lets the loop breathe, degrading the entire HA instance.
Suggested fixes
- Don't full-reload on every
SIGNAL_TIMELINE_UPDATED; debounce, or update the in-memory list incrementally / only recompute the "current event".
- Decouple
_purge_expired_events() from load_events() (run it on a timer, not on every read), and move all blocking file/os.remove work to async_add_executor_job.
- In
async_get_events(), query the DB with a WHERE start/end range filter instead of loading the whole table.
- Resolve the timezone once instead of triggering per-event
zoneinfo opens on the loop.
Workaround
Disable the calendar.llm_vision_timeline entity (Settings → Devices & Services → Entities). llmvision analysis/notifications keep working; only the calendar timeline entity is lost.
Version of the integration
1.7.0
Version of Home Assistant
2026.6.3
Additional context
Install: HA in Docker (Alpine image), SQLite recorder, ~2500 entities. Timeline retention default. Happy to test a patch / provide more logs or a profile.
Describe the bug
After updating to 1.7.0, the
calendar.llm_vision_timelineentity pins one CPU core at ~100% continuously and starves the Home Assistant asyncio event loop. HA logs, on repeat:Because the event loop is saturated, unrelated integrations start timing out (in my case
custom_components.frigate.api: Timeout error fetching ...— Frigate itself responds in <15 ms, the timeout is purely loop starvation).I confirmed the culprit thread by sampling
/proc/<pid>/task/*/statfrom the host: the MainThread (event loop) is the consumer, not the recorder or executor. Disabling thecalendar.llm_vision_timelineentity immediately drops the core to idle (MainThread 66% → 10%, load 1.2 → 0.4) and the Frigate timeouts stop. Re-enabling brings the pegged core back.This is not a data-volume problem: my
events.dbis 1795 rows / 1.2 MB (~1 week of events). A reload over that should be milliseconds.Root cause (code analysis, 1.7.0)
In
calendar.py:Calendar.async_added_to_hass()connectsSIGNAL_TIMELINE_UPDATEDtoasync_schedule_update_ha_state(force_refresh=True)— so every timeline write (i.e. every camera event) forces a fullasync_update.Calendar.async_update()callstimeline.load_events()thenget_all_events()and rebuilds allCalendarEventobjects from scratch each time.Calendar.async_get_events()(called when the calendar card is viewed) also callsload_events()— a full reload per request.In
timeline.py:load_events()(~L595) runsawait self._purge_expired_events()andSELECT uid,title,start,end,... FROM eventswith no filter, then parses every row (dt_util.parse_datetime+_ensure_datetime) on every call._purge_expired_events()runs on everyload_events()and appears to do blocking filesystem work on snapshots (hence the repeated[CLEANUP] Failed to remove ...warnings) plus per-event timezone conversions that trigger blockingzoneinfofile opens on the loop.Net effect: O(n) DB reload + purge + blocking file/
zoneinfoI/O on the event loop, executed on every event write and amplified by event frequency. Even at ~1.8k rows it exceeds the 10 s warning threshold and never lets the loop breathe, degrading the entire HA instance.Suggested fixes
SIGNAL_TIMELINE_UPDATED; debounce, or update the in-memory list incrementally / only recompute the "current event"._purge_expired_events()fromload_events()(run it on a timer, not on every read), and move all blocking file/os.removework toasync_add_executor_job.async_get_events(), query the DB with aWHERE start/endrange filter instead of loading the whole table.zoneinfoopens on the loop.Workaround
Disable the
calendar.llm_vision_timelineentity (Settings → Devices & Services → Entities). llmvision analysis/notifications keep working; only the calendar timeline entity is lost.Version of the integration
1.7.0
Version of Home Assistant
2026.6.3
Additional context
Install: HA in Docker (Alpine image), SQLite recorder, ~2500 entities. Timeline retention default. Happy to test a patch / provide more logs or a profile.