Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto update and expand _lights class variable in AdaptiveSwitch on access #562

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
37 changes: 24 additions & 13 deletions custom_components/adaptive_lighting/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,10 @@ def _get_switches_with_lights(
continue
switch = data[config.entry_id]["instance"]
all_check_lights = _expand_light_groups(hass, lights)
switch._expand_light_groups()
# Check if any of the lights are in the switch's lights
if set(switch._lights) & set(all_check_lights):
if set(switch._lights) & set(
all_check_lights
): # pylint: disable=protected-access
switches.append(switch)
return switches

Expand Down Expand Up @@ -485,7 +486,9 @@ async def handle_apply(service_call: ServiceCall):
lights = data[CONF_LIGHTS]
for switch in switches:
if not lights:
all_lights = switch._lights # pylint: disable=protected-access
all_lights = (
lights or switch._lights
) # pylint: disable=protected-access
else:
all_lights = _expand_light_groups(switch.hass, lights)
switch.turn_on_off_listener.lights.update(all_lights)
Expand Down Expand Up @@ -613,8 +616,8 @@ def _expand_light_groups(hass: HomeAssistant, lights: list[str]) -> list[str]:
if state is None:
_LOGGER.debug("State of %s is None", light)
all_lights.add(light)
elif "entity_id" in state.attributes: # it's a light group
group = state.attributes["entity_id"]
elif ATTR_ENTITY_ID in state.attributes: # it's a light group
group = state.attributes[ATTR_ENTITY_ID]
turn_on_off_listener.lights.discard(light)
all_lights.update(group)
_LOGGER.debug("Expanded %s to %s", light, group)
Expand Down Expand Up @@ -838,6 +841,11 @@ def __init__(
data,
)

def __getattribute__(self, name):
Copy link
Owner

@basnijholt basnijholt Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about creating a property on the switch:

@property
def all_lights(self):
    return self._expand_light_groups()

then use switch.all_lights everywhere?

Or maybe just lights (I not already in use in HA somewhere).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brilliant.

Copy link
Collaborator Author

@th3w1zard1 th3w1zard1 Apr 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to modify all the references with this method.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your reasoning :)

However, a custom __getattribute__ might be hard to overlook when debugging because it is not super obvious what happens from just looking at the code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not looking forward to modifying the other PRs. Perhaps we merge this one last?

if name == "_lights":
self._expand_light_groups()
return object.__getattribute__(self, name)

def _set_changeable_settings(
self,
data: dict,
Expand Down Expand Up @@ -957,12 +965,14 @@ async def async_will_remove_from_hass(self):
self._remove_listeners()

def _expand_light_groups(self) -> None:
all_lights = _expand_light_groups(self.hass, self._lights)
all_lights = _expand_light_groups(
self.hass, object.__getattribute__(self, "_lights")
)
self.turn_on_off_listener.lights.update(all_lights)
self.turn_on_off_listener.set_auto_reset_manual_control_times(
all_lights, self._auto_reset_manual_control_time
)
self._lights = list(all_lights)
object.__setattr__(self, "_lights", list(all_lights))

async def _setup_listeners(self, _=None) -> None:
_LOGGER.debug("%s: Called '_setup_listeners'", self._name)
Expand All @@ -983,10 +993,10 @@ async def _setup_listeners(self, _=None) -> None:

self.remove_listeners.extend([remove_interval, remove_sleep])

if self._lights:
self._expand_light_groups()
lights = self._lights
if lights:
remove_state = async_track_state_change_event(
self.hass, self._lights, self._light_event
self.hass, lights, self._light_event
)
self.remove_listeners.append(remove_state)

Expand All @@ -1008,16 +1018,17 @@ def extra_state_attributes(self) -> dict[str, Any]:
for key in self._settings:
extra_state_attributes[key] = None
return extra_state_attributes
lights = self._lights
extra_state_attributes["manual_control"] = [
light
for light in self._lights
for light in lights
if self.turn_on_off_listener.manual_control.get(light)
]
extra_state_attributes.update(self._settings)
timers = self.turn_on_off_listener.auto_reset_manual_control_timers
extra_state_attributes["autoreset_time_remaining"] = {
light: time
for light in self._lights
for light in lights
if (timer := timers.get(light)) and (time := timer.remaining_time()) > 0
}
return extra_state_attributes
Expand Down Expand Up @@ -1677,7 +1688,7 @@ def _handle_timer(
timer.cancel()
timers_dict.pop(light)
else: # Timer object already exists, just update the delay and restart it
timer.delay = delay
timer.delay = delay # pylint: disable=protected-access
timer.start()
elif delay is not None: # Timer object does not exist, create it
timer = _AsyncSingleShotTimer(delay, reset_coroutine)
Expand Down