Skip to content

fix: replace deprecated async_track_state_change with async_track_state_change_event#94

Open
r0bb10 wants to merge 1 commit into
andbad:mainfrom
r0bb10:fix/async-track-state-change
Open

fix: replace deprecated async_track_state_change with async_track_state_change_event#94
r0bb10 wants to merge 1 commit into
andbad:mainfrom
r0bb10:fix/async-track-state-change

Conversation

@r0bb10

@r0bb10 r0bb10 commented Jul 6, 2026

Copy link
Copy Markdown

Async_track_state_change was deprecated in HA 2025.5 and scheduled for removal. This fix uses async_track_state_change_event on supported HA versions while falling back to the old function on older instances.

Adds a try/except import for forward compatibility and an event adapter wrapper that extracts entity_id/old_state/new_state from the Event object to reuse the existing callback logic unchanged.

…te_change_event

async_track_state_change was deprecated in HA 2025.5 and scheduled for
removal. This fix uses async_track_state_change_event on supported HA
versions while falling back to the old function on older instances.

Adds a try/except import for forward compatibility and an event adapter
wrapper that extracts entity_id/old_state/new_state from the Event object
to reuse the existing callback logic unchanged.
@andbad

andbad commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Hi, thanks for tackling this deprecation but there are a couple of issues that prevent merging as-is.

1. Unconditional import of async_track_state_change

In the except ImportError branch you set async_track_state_change_event = None, but then immediately import async_track_state_change unconditionally (outside the except block). On HA ≥ 2025.5 that function has been removed, so the import will raise ImportError on exactly the instances this fix is supposed to support.

2. The if async_track_state_change_event branch in the call site

Scattering the version check across every registration point makes the code fragile: any future call site would need the same if/else duplicated. The cleaner pattern is to keep the shim entirely inside the except block so async_track_state_change_event is always a valid callable, and the rest of the code stays uniform.

A working fallback would look like this:

try:
    from homeassistant.helpers.event import async_track_state_change_event
except ImportError:
    from homeassistant.helpers.event import async_track_state_change as _legacy

    class _LegacyEvent:
        def __init__(self, entity_id, old_state, new_state):
            self.data = {"entity_id": entity_id, "old_state": old_state, "new_state": new_state}

    def async_track_state_change_event(hass, entity_ids, action):
        @callback
        def _wrap(entity_id, old_state, new_state):
            action(_LegacyEvent(entity_id, old_state, new_state))
        return _legacy(hass, entity_ids, _wrap)

With this shim in place, the call site needs no conditional at all. Let me know

By(t)e

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants