mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-12 06:44:04 +02:00
add take_over_control feat
This commit is contained in:
parent
d29649bdfd
commit
3eb593ec6d
4 changed files with 59 additions and 16 deletions
|
|
@ -29,7 +29,7 @@ CONF_SUNRISE_OFFSET, DEFAULT_SUNRISE_OFFSET = "sunrise_offset", 0
|
|||
CONF_SUNRISE_TIME = "sunrise_time"
|
||||
CONF_SUNSET_OFFSET, DEFAULT_SUNSET_OFFSET = "sunset_offset", 0
|
||||
CONF_SUNSET_TIME = "sunset_time"
|
||||
CONF_TAKE_OVER_CONTROL, DEFAULT_TAKE_OVER_CONTROL = "take_over_control", True
|
||||
CONF_TAKE_OVER_CONTROL, DEFAULT_TAKE_OVER_CONTROL = "take_over_control", False
|
||||
CONF_TRANSITION, DEFAULT_TRANSITION = "transition", 60
|
||||
|
||||
ATTR_TURN_ON_OFF_LISTENER = "turn_on_off_listener"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
"sunrise_time": "sunrise_time, in 'HH:MM:SS' format",
|
||||
"sunset_offset": "sunset_offset, in +/- seconds",
|
||||
"sunset_time": "sunset_time, in 'HH:MM:SS' format",
|
||||
"take_over_control": "take_over_control, (NOT YET IMPLEMENTED!) if manually adjusting the lights when they are already on",
|
||||
"take_over_control": "take_over_control, (beta feature) if manually adjusting the lights when they are already on",
|
||||
"transition": "transition, in seconds"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
# Locks that prevent light adjusting when waiting for a light to 'turn_off'
|
||||
self._locks: Dict[str, asyncio.Lock] = {}
|
||||
# To identify that this integration made a change
|
||||
self._context = Context()
|
||||
self.__context = Context() # self._context will be overwritten
|
||||
|
||||
# Set in self._update_attrs_and_maybe_adapt_lights
|
||||
self._light_settings = {}
|
||||
|
|
@ -370,6 +370,10 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
return {key: None for key in self._light_settings}
|
||||
return self._light_settings
|
||||
|
||||
def _reset_take_over_control(self):
|
||||
for light in self._lights:
|
||||
self.turn_on_off_listener.manually_controlled[light] = False
|
||||
|
||||
async def async_turn_on( # pylint: disable=arguments-differ
|
||||
self, adapt_lights: bool = True
|
||||
) -> None:
|
||||
|
|
@ -380,6 +384,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
if self.is_on:
|
||||
return
|
||||
self._state = True
|
||||
self._reset_take_over_control()
|
||||
await self._setup_listeners()
|
||||
if adapt_lights:
|
||||
await self._update_attrs_and_maybe_adapt_lights(
|
||||
|
|
@ -392,6 +397,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
return
|
||||
self._state = False
|
||||
self._remove_listeners()
|
||||
self._reset_take_over_control()
|
||||
|
||||
async def _async_update_at_interval(self, now=None) -> None:
|
||||
await self._update_attrs_and_maybe_adapt_lights(force=False)
|
||||
|
|
@ -450,7 +456,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
LIGHT_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
service_data,
|
||||
context=self._context,
|
||||
context=self.__context,
|
||||
)
|
||||
|
||||
async def _update_attrs_and_maybe_adapt_lights(
|
||||
|
|
@ -469,23 +475,32 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
lights = self._lights
|
||||
if (self._only_once and not force) or not lights:
|
||||
return
|
||||
await self._adapt_lights(lights, transition)
|
||||
await self._adapt_lights(lights, transition, force)
|
||||
|
||||
async def _adapt_lights(
|
||||
self, lights: List[str], transition: Optional[int]
|
||||
self, lights: List[str], transition: Optional[int], force: bool
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"%s: '_adapt_lights(%s, %s)' called", self.name, lights, transition
|
||||
"%s: '_adapt_lights(%s, %s, %s)' called",
|
||||
self.name,
|
||||
lights,
|
||||
transition,
|
||||
force,
|
||||
)
|
||||
for light in lights:
|
||||
if not is_on(self.hass, light):
|
||||
continue
|
||||
if self._take_over_control:
|
||||
if await self.turn_on_off_listener.is_manually_adjusted(
|
||||
if self.turn_on_off_listener.is_manually_controlled(
|
||||
light,
|
||||
off_to_on_event=self._off_to_on_event.get(light),
|
||||
adaptive_lighting_context=self._context,
|
||||
force,
|
||||
adaptive_lighting_context=self.__context,
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"%s: '%s' is being manually controlled, stop adapting.",
|
||||
self._name,
|
||||
light,
|
||||
)
|
||||
continue
|
||||
await self._adapt_light(light, transition)
|
||||
|
||||
|
|
@ -493,6 +508,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
if not match_state_event(event, ("on", "off")):
|
||||
return
|
||||
_LOGGER.debug("%s: _sleep_state_event, event: '%s'", self._name, event)
|
||||
self._reset_take_over_control()
|
||||
await self._update_attrs_and_maybe_adapt_lights(
|
||||
transition=self._initial_transition, force=True
|
||||
)
|
||||
|
|
@ -540,6 +556,7 @@ class AdaptiveSwitch(SwitchEntity, RestoreEntity):
|
|||
):
|
||||
# Tracks 'off' → 'on' state changes
|
||||
self._on_to_off_event[entity_id] = event
|
||||
self.turn_on_off_listener.manually_controlled[entity_id] = False
|
||||
|
||||
|
||||
class AdaptiveSleepModeSwitch(SwitchEntity, RestoreEntity):
|
||||
|
|
@ -752,6 +769,8 @@ class TurnOnOffListener:
|
|||
self.turn_on_event: Dict[str, Event] = {}
|
||||
# Keeps 'asyncio.sleep` tasks that can be cancelled by 'light.turn_on' events
|
||||
self.sleep_tasks: Dict[str, asyncio.Task] = {}
|
||||
# Tracks which lights are manually controlled
|
||||
self.manually_controlled: Dict[str, bool] = {}
|
||||
|
||||
self.remove_listener = self.hass.bus.async_listen(
|
||||
EVENT_CALL_SERVICE, self.turn_on_off_event_listener
|
||||
|
|
@ -782,6 +801,7 @@ class TurnOnOffListener:
|
|||
)
|
||||
for eid in entity_ids:
|
||||
self.turn_off_event[eid] = event
|
||||
self.manually_controlled[eid] = False
|
||||
|
||||
elif service == SERVICE_TURN_ON:
|
||||
_LOGGER.debug("Detected an 'light.turn_on('%s')' event", entity_ids)
|
||||
|
|
@ -791,12 +811,35 @@ class TurnOnOffListener:
|
|||
task.cancel()
|
||||
self.turn_on_event[eid] = event
|
||||
|
||||
async def is_manually_adjusted(self, light: str, off_to_on_event: Optional[Event], adaptive_lighting_context: Context):
|
||||
def is_manually_controlled(
|
||||
self,
|
||||
light: str,
|
||||
force: bool,
|
||||
adaptive_lighting_context: Context,
|
||||
):
|
||||
"""Check if the light has been 'on' and is now manually being adjusted."""
|
||||
if off_to_on_event is None:
|
||||
# No state change has been registered before, so we can't tell.
|
||||
return False
|
||||
return False
|
||||
manually_controlled = self.manually_controlled[light]
|
||||
if manually_controlled:
|
||||
# Manually controlled until light is turned on and off
|
||||
return True
|
||||
|
||||
turn_on_event = self.turn_on_event.get(light)
|
||||
if (
|
||||
turn_on_event is not None
|
||||
and adaptive_lighting_context.id != turn_on_event.context.id
|
||||
and not force
|
||||
):
|
||||
# Light was already on and 'light.turn_on' was not called by
|
||||
# the adaptive_lighting integration.
|
||||
manually_controlled = self.manually_controlled[light] = True
|
||||
_LOGGER.debug(
|
||||
"'%s' was already on and 'light.turn_on' was not called by the"
|
||||
" adaptive_lighting integration, the Adaptive Lighting will stop"
|
||||
" adapting the light until the switch or the light turns off and"
|
||||
" then on again.",
|
||||
light,
|
||||
)
|
||||
return manually_controlled
|
||||
|
||||
async def maybe_cancel_adjusting(
|
||||
self, entity_id: str, off_to_on_event: Event, on_to_off_event: Optional[Event]
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
"sunrise_time": "sunrise_time, in 'HH:MM:SS' format",
|
||||
"sunset_offset": "sunset_offset, in +/- seconds",
|
||||
"sunset_time": "sunset_time, in 'HH:MM:SS' format",
|
||||
"take_over_control": "take_over_control, (NOT YET IMPLEMENTED!) if manually adjusting the lights when they are already on",
|
||||
"take_over_control": "take_over_control, (beta feature) if manually adjusting the lights when they are already on",
|
||||
"transition": "transition, in seconds"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue