diff --git a/apps/controller.py b/apps/controller.py index 6b1b221..ad5cdea 100644 --- a/apps/controller.py +++ b/apps/controller.py @@ -20,12 +20,13 @@ class ControllerEntities(Hass): for entity in self.entities: assert self.entity_exists(entity), f'{entity} does not exist' - self.entities = [self.get_entity(e) for e in self.entities] @dataclass(init=False) class ControllerRoomLights(ControllerEntities): + sleep: str + def initialize(self): super().initialize() self.log(f'Initialized light controller for {[e.friendly_name for e in self.entities]}') @@ -33,7 +34,10 @@ class ControllerRoomLights(ControllerEntities): self.register_service(f'{self.name}/deactivate', self.deactivate) def activate(self, namespace: str = None, domain: str = None, service=None, kwargs=None): - self.log(self.entities) + # if self.is_sleeping: + # self.log(f'Sleep mode is on, returning early') + # return + for entity in self.entities: self.log(f'Turning on {entity.name}') entity.turn_on() @@ -48,6 +52,17 @@ class ControllerRoomLights(ControllerEntities): def state(self) -> bool: return any([e.get_state() == 'on' for e in self.entities]) + @property + def is_sleeping(self) -> bool: + if 'sleep' in self.args: + return self.get_entity(self.args['sleep']).is_state('on') + else: + return False + + @is_sleeping.setter + def is_sleeping(self, val: bool): + if 'sleep' in self.args: + self.get_entity(self.args['sleep']).set_state(state='on' if val else 'off') @dataclass(init=False) class ControllerMotion(ControllerEntities): @@ -97,7 +112,8 @@ class ControllerMotion(ControllerEntities): def callback_motion_on(self, entity, attribute, old, new, kwargs): self.log(f'Motion detected on {self.friendly_name(entity)}') - self.room.activate() + if not self.room.is_sleeping: + self.room.activate() def callback_motion_off(self, entity, attribute, old, new, kwargs): self.log(f'Motion stopped on {self.friendly_name(entity)} for {self.off_duration}') @@ -132,24 +148,47 @@ class ControllerButton(Hass): else: self.room.activate() + # double click + elif data['event'] == 1004: + self.log(f'{data["id"]} double click') + self.room.is_sleeping = not self.room.is_sleeping + if not self.room.is_sleeping: + self.room.activate() + @dataclass(init=False) -class ControllerDaylight(ControllerEntities): +class ControllerDaylight(Hass): + room: ControllerRoomLights + entities: List[str] latitude: float longitude: float + enable: bool = field(init=False, default=True) def initialize(self): - super().initialize() + # convert room to App + self.room: ControllerRoomLights = self.get_app(self.args['room']) + + # convert entities + for entity in self.args['entities']: + assert self.entity_exists(entity), f'{entity} does not exist' + self.entities = [self.get_entity(e) for e in self.args['entities']] + # self.log(self.entities) + + # create Adjuster self.adjuster = DaylightAdjuster( latitude=self.args['latitude'], longitude=self.args['longitude'], periods=self.args['periods'], resolution=500 ) - self.log(self.adjuster) + # self.log(self.adjuster) self.listen_state(callback=self.handle_state_change, entity_id=[e.entity_id for e in self.entities]) - self.log(f'Listening for state {[e.friendly_name for e in self.entities]}') + ents = [e.friendly_name for e in self.entities] + if len(ents) > 1: + ents[-1] = f'and {ents[-1]}' + delim = ', ' if len(ents) >= 3 else ' ' + self.log(f'Listening for state changes on {delim.join(ents)}') self.run_every(callback=self.update_sensors, start='now', interval=5.0) @@ -165,9 +204,18 @@ class ControllerDaylight(ControllerEntities): else: if hasattr(self, 'adjustment_handle'): self.cancel_timer(self.adjustment_handle) + del self.adjustment_handle self.log(f'Cancelled adjustments') - def matching_state(self, entity_id: str): + def matching_state(self, entity_id: str) -> bool: + """Checks whether the current state of the light matches the settings from the DaylightAdjuster + + Args: + entity_id (str): full entity ID + + Returns: + bool + """ state = self.get_state(entity_id=entity_id, attribute='all')['attributes'] settings = self.adjuster.current_settings try: @@ -181,8 +229,8 @@ class ControllerDaylight(ControllerEntities): def ongoing_adjustment(self, kwargs): self.log(f'Ongoing adjustment') settings = self.adjuster.current_settings - valid = self.matching_state(entity_id=kwargs['entity']) - if not valid: + matching = self.matching_state(entity_id=kwargs['entity']) + if not matching and not self.room.is_sleeping: self.turn_on(entity_id=kwargs['entity'], **settings) self.log(f'Adjusted {self.friendly_name(kwargs["entity"])} with {settings}')