This release replaces the heuristics used in v2.7.1 and v2.7.2 with a structural fix for the multi-device MQTT sync bug in #112, and restores timer persistence for cards that use plain browser storage (#114).
Why the earlier MQTT fixes did not hold
Both previous attempts gated on "did this data come from the sensor entity". A phone resuming from the background still holds its pre-sleep hass state, so that check passed and the old snapshot was treated as authoritative. It could then ring an already-dismissed timer and publish itself back over the retained topic, which every other device accepted.
There is no reliable way for a client to tell "nothing changed" from "I missed updates" using its own last-seen version, so staleness detection alone can never fully close this.
🐛 Fixed
- A device waking up can no longer resurrect a dismissed timer (#112). Reading the retained topic is now a reconciliation instead of a replacement. Each timer carries a revision, and dismissing one publishes a tombstone that outranks any timer at or below that revision. A card that slept through a dismissal cannot bring the timer back, and the first card to notice a resurrected entry republishes the corrected list, so the retained topic repairs itself.
- Ordering no longer depends on device clocks. Revisions are a Lamport counter rather than a wall-clock timestamp. This matters because the stale republish happens later in real time than the dismissal it was undoing, so a timestamp comparison picked the wrong winner.
- No phantom alarm on wake. The card detects the gap in its own tick loop that a freeze leaves behind and holds MQTT data as non-authoritative until the websocket resyncs, so a woken device does not ring a timer that is already gone. A genuinely expired timer still rings once state settles.
- Local-storage timers survive a reload again (#114). Since v2.4.0 a card with no
storage_namespace, nodefault_timer_entityand noentitiesfell back to a random key that was regenerated on every construction, so its timers disappeared whenever the dashboard was reopened or navigated away from, and a deadlocalStorageentry was left behind each time. The automatic namespace is now derived from the card's own configuration, so it stays the same across reloads while still differing between differently configured cards, which preserves the isolation added for #94. - Pinned timer identity is stable. Pinned ids no longer mix in the random per-instance key, so a timer started from a pinned preset keeps its identity across reloads.
- Orphaned
simple-timer-card-instance-*keys left by 2.4.0 through 2.8.0 are cleaned up once per page load.
🔧 Behavior
- No configuration changes. Existing MQTT setups, including the
default_timer_entity: sensor.*shorthand, keep working as-is. - The MQTT payload gains a
deletedkey next totimers. It is additive: older cards and the failsafe automation inmqtt-hybrid-model.mdreadtimersand are unaffected. Tombstones are pruned after 24 hours. - Deletions made by cards that do not write tombstones, such as an older version during a rollout, are still honoured.
compatibility_mode: latestpublishes a bare array, which cannot carry tombstones, so it keeps the previous behavior.- Two local-storage cards whose configuration is byte for byte identical now resolve to the same namespace and share state. Give one of them an explicit
storage_namespace. Note also that editing a card without an explicit namespace changes its derived key, which starts it on a fresh store, sostorage_namespaceis still worth setting on a card you intend to keep.
🧪 Testing this
- #112: start a timer, let it expire and dismiss it on one device, then wake a second device that was backgrounded. Previously the alarm restarted and every device flipped back to expired. Expected now: the woken device settles to no timer, and other devices stay unchanged.
- #114: add a card with no
storage_namespace,default_timer_entityorentities, start a timer, then navigate away and back or reload the browser. The timer should still be there.
Notes
- Published as a pre-release for multi-device testing before promotion to stable.
Full Changelog: v2.7.4...v2.8.0