github sfox38/Time_Off v2.0.6

one day ago

Changelog

v2.0.6

  • [Improvement] Entity rename auto-follow - if the managed device entity is renamed in the entity registry, Time Off now migrates the persisted expiry to the new entity ID and schedules a reload, keeping state consistent without manual intervention.
  • [Improvement] Trigger Only state read from entity object reference - previously read from the HA state machine via string lookup; now reads directly from the registered switch entity object, eliminating a class of subtle timing failures during startup recovery.
  • [Improvement] Off After default read from entity object reference - same improvement as above for the Time Off number entity.
  • [Improvement] Ghost entity cleanup on setup - stale entities from a previous load that are no longer in the current target list are removed from the entity registry automatically on async_setup_entry.
  • [Improvement] CONFIG_SCHEMA added - registers cv.config_entry_only_config_schema so HA correctly reports that this integration does not support YAML configuration.
  • [Improvement] Device trigger strings added - strings.json now includes the device_automation.trigger_type section required for the UI device trigger label ("Time Off: When timer expires") to render correctly.
  • [Improvement] Service descriptions added to strings.json - all three services now have name, description, and field metadata, enabling documentation in the HA Services UI.
  • [Fix] Device triggers never fired - the time_off_timer_expired device trigger automation option was silently broken in rare cases. The event config was passed to HA's event trigger engine as a plain string rather than a validated schema object, causing the event type to be iterated character by character. Fixed by running the trigger config through event_trigger.TRIGGER_SCHEMA before passing it to async_attach_trigger.
  • [Fix] Reload after unload orphaned the device - cancelling timers during async_unload_entry incorrectly triggered the loop's cleanup guard (which clears the persisted expiry), so after a reload the device had no saved state and appeared as if it had never had a timer. Fixed by popping tasks from the active-timers dict before cancelling, matching the behaviour of the normal stop-timer path.
  • [Fix] Stale "Timer Active" after device turned off during shutdown - if a device was turned off while HA was down, the saved expiry was still present on next boot, leaving Timer Active stuck on with no countdown running. Fixed: recovery now explicitly clears stale expiries for devices that are off at startup.
  • [Fix] Recovery task not cancelled on unload - the startup recovery background task was not stored or cancelled when an entry was unloaded, leaving a dangling task that could act on a stale entry. Fixed by storing the task handle in entry data and cancelling it in async_unload_entry.
  • [Fix] Off After restore incorrectly set a non-zero value on a fresh boot - the Off After sensor restored its previous value even when no timer was actually running (no matching entry in the persisted expiry store). It now only restores a non-zero value if a saved expiry exists for that device.
  • [Fix] Trigger Only switch restore used a hardcoded string - the on/off restore comparison used a literal "on" string instead of the STATE_ON constant, making it fragile against HA internals. Fixed.
  • [Fix] Binary sensor interval handle was not initialised before async_added_to_hass - a missing self._unsub_interval = None in __init__ meant an AttributeError could occur if async_will_remove_from_hass ran before the entity was fully added. Fixed.
  • [Fix] Binary sensor _update scheduled a coroutine per tick - the interval callback was an async def, causing HA to allocate a task on every polling tick. Converted to a synchronous @callback since the method only calls synchronous HA helpers.
  • [Fix] Binary sensor did not recover its polling interval on restart - if a timer was already active when the binary sensor entity loaded (e.g. after a reload), the remaining attribute stayed stale. Fixed by starting the update interval at the end of async_added_to_hass when the timer is already active.
  • [Fix] Time Off number entity used a redundant state backing field - number.py maintained a private _state field alongside _attr_native_value, causing a double-write on every value change. Simplified to use _attr_native_value directly.
  • [Fix] Services registered per config entry instead of once at integration load - services were re-registered on every async_setup_entry call and only conditionally removed, leading to duplicate registration warnings and broken removal logic. All three services are now registered once in async_setup.
  • [Fix] Config entry title was overwritten on every reload - async_setup_entry unconditionally called async_update_entry with a freshly computed title, overwriting any name the user had set. Removed; the title is now set once at creation and never touched again.
  • [Fix] already_configured abort used wrong strings.json key - the abort reason key was nested under "error" instead of "abort", so the duplicate-device message was never shown. Fixed.
  • [Doc] Universal Functionality table - added Climate and Input Boolean to the supported device list.
  • [Doc] Restart recovery description - clarified that the 30-second wait is a fixed sleep (not adaptive), that it only runs when timers were active at shutdown, and documented the Trigger Only recovery path (fires event + resumes loop if device is still on).
  • [Doc] Various grammar and wording fixes.

Don't miss a new Time_Off release

NewReleases is sending notifications on new releases.