first version of presence simulation with light

This commit is contained in:
2026-06-21 12:26:42 +02:00
parent 6310e39fd9
commit 3087ad1f80
4 changed files with 694 additions and 1 deletions

View File

@@ -114,6 +114,8 @@ from ad_toolbox.eventhandler import EventHandler
class SmartLight(SmartSwitch):
def on_initialize_smart_object(self):
self.presence_simulation_slots_active = set()
# light_brightness_pct_list : ordered list of (Evaluator, label) pairs
# light_brightness_pct : currently active brightness % (str label)
self.light_brightness_pct_list = list()
@@ -196,6 +198,15 @@ class SmartLight(SmartSwitch):
# SmartSwitch overrides
# ------------------------------------------------------------------
# While simulation is active for this light, ignore auto-off transitions
# from smart_conditions to keep deterministic on-duration windows.
def on_smart_conditions_change(self,prev_result,result):
if len(self.presence_simulation_slots_active) > 0:
if result == SmartCondition.Result.Succeeded and self.is_off():
self.switch_on()
return
super().on_smart_conditions_change(prev_result,result)
# Override: apply light_brightness_pct when turning on, if one is active.
def switch_on(self):
if self.light_brightness_pct != None:
@@ -225,4 +236,61 @@ class SmartLight(SmartSwitch):
self.log_info("Turning off")
self.call_service("light/turn_off", entity_id = self.entity_id)
# ------------------------------------------------------------------
# Presence simulation API (called by HomeAlarm)
# ------------------------------------------------------------------
def start_presence_simulation(self, source_app=None, slot_name=None, payload=None):
if slot_name:
self.presence_simulation_slots_active.add(slot_name)
if not isinstance(payload, dict):
payload = {}
brightness_pct = payload.get('brightness_pct')
transition = payload.get('transition')
if brightness_pct is None and transition is None:
self.switch_on()
return
service_kwargs = {'entity_id': self.entity_id}
if brightness_pct is not None:
service_kwargs['brightness_pct'] = brightness_pct
if transition is not None:
service_kwargs['transition'] = transition
self.call_service("light/turn_on", **service_kwargs)
def stop_presence_simulation(self, source_app=None, slot_name=None, payload=None):
if slot_name and slot_name in self.presence_simulation_slots_active:
self.presence_simulation_slots_active.remove(slot_name)
if not isinstance(payload, dict):
payload = {}
if payload.get('keep_on', False):
return
transition = payload.get('transition')
if transition is None:
self.switch_off()
else:
self.call_service(
"light/turn_off",
entity_id=self.entity_id,
transition=transition,
)
# If no simulation slot is still active, re-apply current automation
# state so the light immediately converges to the expected result.
if len(self.presence_simulation_slots_active) == 0 and self.smart_conditions_evaluator:
result = self.smart_conditions_evaluator.evaluate(False)
if result == SmartCondition.Result.Succeeded:
if self.is_off():
self.switch_on()
else:
if self.is_on():
self.switch_off()