v5.4.0
First-come chores can now stay on the card after someone claims them, so the rest of the pool sees who won instead of watching the chore vanish. Alongside that, calendar and availability state writes no longer block Home Assistant's event loop — if your log has been filling with "Updating state for calendar.taskmate_… took 1.4 seconds", that stops here.
Both changes are drop-in. The new card option defaults to today's behaviour, and the performance work is invisible apart from the warnings going away. No configuration changes are required to upgrade.
New — cards
first_come_claimed_mode on the child card
A first_come chore is a single shared quota of one. The moment anyone completed it, the row disappeared from every card in the pool — the winner's included. There was no confirmation it had been done, and no way to see who got there first.
first_come_claimed_mode controls what the pool sees once a chore is claimed:
| Value | Behaviour |
|---|---|
hide |
Default. Row is removed once claimed — exactly as before. |
dim |
Row stays, greyed out and not tappable. |
show |
Row stays at full opacity, still not tappable. |
Under First-Come Chores — When Claimed in the card editor, or in YAML:
type: custom:taskmate-child-card
entity: sensor.taskmate_overview
child_id: a8c8376a
first_come_claimed_mode: dim # hide (default) | dim | showUnder dim and show, the winner keeps their normal completed row with undo. Everyone else gets a locked row reading "Already done by {name}". A pool-wide dismissal from the parent panel has no specific winner, so every child sees "Already claimed today" rather than it showing as done for all of them.
Honoured by the standard chore row and all five designs (classic, playroom, console, cleanpro, accessible). Known limitation: _renderTimedChoreCard and _renderPreReaderTile don't check this lock — or recurrence_done_mode — so a claimed first-come timed chore or picture-mode tile stays live and tappable in those two views.
Thanks to @d1g1talweb for this one. (#821, closes #822)
Fixes
Calendar and availability state writes blocked the event loop
Every coordinator refresh logged:
Updating state for calendar.taskmate_xxx (TaskMateCalendar) took 1.096 seconds
Updating state for sensor.taskmate_chore_availability (...) took 2.083 seconds
Both entities rebuilt the entire dataset from raw dictionaries on every read. Profiled against a synthetic 6-children × 120-chore × 20,000-completion family, a single read of calendar.event made 24.1 million function calls — 157k Chore.from_dict, 258k Child.from_dict, 415k uuid4().
Four separate defects fed it:
| # | Defect | Effect |
|---|---|---|
| 1 | The calendar re-projected its full 60-day horizon on every read, and HA reads it twice per write (state, then attributes) | 2× the worst cost, every refresh |
| 2 | The projection ran outside availability_build_scope, so balanced-mode grouping and the assignment pool re-fetched and rebuilt every stored dataclass per (chore, day) pair |
1,311 full chore-list rebuilds for one read |
| 3 | _is_rotation_done_today walked the whole completions list per chore |
availability matrix was O(chores × completions) |
| 4 | data.get("id", generate_id()) evaluated its default eagerly |
415k uuid4() calls burned and discarded per read |
The availability sensor separately rebuilt in native_value the matrix extra_state_attributes had just built, doubling every write.
The calendar horizon is now memoised on (day, id(coordinator.data), external_state_version) — the same cache key _CachedAttrsSensor already uses, so an availability or visibility flip still invalidates it correctly. (#824, fixes #823)
Internal
- A performance regression test covering the state-write path, so the projection cannot quietly start rebuilding per read again. (#824)
- ESLint 10.8.0 → 10.8.1 and a
globalsbump. (#813)
Verification
Exercised on a live Home Assistant test instance before release:
- A real
first_comechore claimed by one child in a two-child pool, rendered across all five designs × all three modes.hideremoves the row for everyone;dim/showkeep it, winner on their completed/undo row, other child on a locked "Already done by Vaiha" row. Clicking every node of the claimed row fires zero service calls in all five designs. - Restarted onto this build: 61 TaskMate entities load, both calendars and the availability sensor report normally, and the error log carries no
took N secondswarnings. - Full test suite: 1659 passed.
Upgrading
Install through HACS as usual, then restart Home Assistant. Nothing to reconfigure — first_come_claimed_mode defaults to hide, which is the behaviour you already have.
Full changelog: v5.3.0...v5.4.0