Source code for aioafero.v1.controllers.split_refresh

"""Refresh filtered states on split clones after the parent metadevice changes.

Split callbacks already build clones with filtered states during discovery. After
an in-place parent patch (REST write echo or Conclave push), existing clones must
be re-filtered from the parent before ``RESOURCE_UPDATE_RESPONSE`` events fire.

Controller imports are deferred so this module cannot participate in an import
cycle with :mod:`event` / :mod:`base` (those controllers import ``event``).
"""

from __future__ import annotations

from collections.abc import Callable
from functools import lru_cache

from aioafero.device import AferoDevice, AferoState

SplitStateRefresher = Callable[[AferoDevice, str], list[AferoState]]


def _refresh_security_sensor_states(
    parent: AferoDevice, instance: str
) -> list[AferoState]:
    from .security_system import (  # noqa: PLC0415 - deferred to break import cycles
        get_valid_states as security_sensor_valid_states,
    )

    return security_sensor_valid_states(parent.states, int(instance))


@lru_cache(maxsize=1)
def _split_state_refreshers() -> dict[str, SplitStateRefresher]:
    """Build the refresher map once controllers have finished importing."""
    # Deferred imports: controllers import ``event``, which imports this module.
    from .exhaust_fan import (  # noqa: PLC0415
        SPLIT_IDENTIFIER as EXHAUST_FAN_SPLIT,
        get_valid_states as exhaust_fan_valid_states,
    )
    from .light import (  # noqa: PLC0415
        SPLIT_IDENTIFIER as LIGHT_SPLIT,
        get_valid_states as light_valid_states,
    )
    from .portable_ac import (  # noqa: PLC0415
        SPLIT_IDENTIFIER as PORTABLE_AC_SPLIT,
        get_valid_states as portable_ac_valid_states,
    )
    from .security_system import SENSOR_SPLIT_IDENTIFIER  # noqa: PLC0415

    return {
        LIGHT_SPLIT: light_valid_states,
        SENSOR_SPLIT_IDENTIFIER: _refresh_security_sensor_states,
        EXHAUST_FAN_SPLIT: exhaust_fan_valid_states,
        PORTABLE_AC_SPLIT: lambda parent, _instance: portable_ac_valid_states(parent),
    }


[docs] def split_instance_from_device(device: AferoDevice) -> str | None: """Return the split-zone instance key for a clone.""" if device.split is None: return None return device.split.instance
[docs] def refresh_split_clone_states(parent: AferoDevice, clone: AferoDevice) -> None: """Copy filtered parent states onto a split clone before emitting events.""" instance = split_instance_from_device(clone) if instance is None or not clone.split_identifier: return refresher = _split_state_refreshers().get(clone.split_identifier) if refresher is None: return clone.states = refresher(parent, instance)