started entities and custom services in new namespace

This commit is contained in:
John Lancaster
2024-07-25 00:25:08 -05:00
parent 043402ad2f
commit a703fd15fb

View File

@@ -2,12 +2,14 @@ import datetime
import json
import logging
import logging.config
import traceback
from copy import deepcopy
from typing import Dict, List
from functools import wraps
from typing import Any, Dict, List
from appdaemon.entity import Entity
from appdaemon.plugins.hass.hassapi import Hass
from appdaemon.plugins.mqtt.mqttapi import Mqtt
from astral.location import Location
from . import console
from .model import ControllerStateConfig, RoomControllerConfig
@@ -15,7 +17,7 @@ from .model import ControllerStateConfig, RoomControllerConfig
logger = logging.getLogger(__name__)
class RoomController(Hass, Mqtt):
class RoomController(Hass):
"""Class for linking room's lights with a motion sensor.
- Separate the turning on and turning off functions.
@@ -34,12 +36,30 @@ class RoomController(Hass, Mqtt):
assert all(isinstance(s, ControllerStateConfig) for s in new), f'Invalid: {new}'
self._room_config.states = new
@property
@wraps(Location.time_at_elevation)
def time_at_elevation(self):
return self.AD.sched.location.time_at_elevation
@property
def state_entity(self) -> Entity:
return self.get_entity(f'{self.name}.state')
def initialize(self):
self.set_namespace('controller')
self.logger = console.load_rich_config(self.name)
self.set_log_level('DEBUG')
self.register_service(f'{self.name}/activate', self.service_activate)
self.register_service(f'{self.name}/deactivate', self.service_deactivate)
self.app_entities = self.gather_app_entities()
# self.log(f'entities: {self.app_entities}')
self.refresh_state_times()
self.run_daily(callback=self.refresh_state_times, start='00:00:00')
self.log(f'Initialized [bold green]{type(self).__name__}[/]')
def terminate(self):
@@ -82,46 +102,60 @@ class RoomController(Hass, Mqtt):
for state in self._room_config.states:
if state.time is None and state.elevation is not None:
state.time = self.AD.sched.location.time_at_elevation(
transition_time = self.time_at_elevation(
elevation=state.elevation, direction=state.direction
).time()
elif isinstance(state.time, str):
state.time = self.parse_time(state.time)
transition_time = self.parse_time(state.time)
assert isinstance(state.time, datetime.time), f'Invalid time: {state.time}'
self.states = sorted(self.states, key=lambda s: s.time, reverse=True)
# schedule the transitions
for state in self.states[::-1]:
# t: datetime.time = state['time']
t: datetime.time = state.time
try:
self.run_at(
callback=self.activate_any_on,
start=t.strftime('%H:%M:%S'),
cause='scheduled transition',
callback=lambda cb_args: self.set_controller_scene(cb_args['state']),
start=transition_time.strftime('%H:%M:%S'),
state=state,
)
except ValueError:
# happens when the callback time is in the past
pass
except Exception as e:
self.log(f'Failed with {type(e)}: {e}')
def current_state(self, now: datetime.time = None) -> ControllerStateConfig:
def set_controller_scene(self, state: ControllerStateConfig):
try:
self.state_entity.set_state(attributes=state.model_dump())
except Exception:
self.logger.error(traceback.format_exc())
else:
self.log(f'Set controller state of {self.name}: {state.model_dump()}', level='DEBUG')
def current_state(self) -> ControllerStateConfig:
if self.sleep_bool():
self.log('sleep: active')
self.log('sleep: active', level='DEBUG')
if state := self.args.get('sleep_state'):
return ControllerStateConfig(**state)
else:
return ControllerStateConfig(scene={})
return ControllerStateConfig()
else:
now = now or self.get_now().time().replace(microsecond=0)
self.log(f'Getting state for {now.strftime("%I:%M:%S %p")}', level='DEBUG')
attrs = self.state_entity.get_state('all')['attributes']
return ControllerStateConfig.model_validate(attrs)
state = self._room_config.current_state(now)
self.log(f'Current state: {state.time}', level='DEBUG')
return state
def current_scene(self, transition: int = None) -> Dict[str, Any]:
state = self.current_state()
if isinstance(state.scene, str):
return state.scene
elif isinstance(state.scene, dict):
return state.to_apply_kwargs(transition)
def service_activate(self, namespace: str, domain: str, service: str, kwargs: Dict[str, Any]):
scene = self.current_scene(transition=0)
if isinstance(scene, str):
self.turn_on(scene)
elif isinstance(scene, dict):
self.call_service('scene/apply', namespace='default', **scene)
def service_deactivate(self, namespace: str, domain: str, service: str, kwargs: Dict[str, Any]):
for e in self.app_entities:
self.turn_off(e)
def app_entity_states(self) -> Dict[str, str]:
states = {entity: self.get_state(entity) for entity in self.app_entities}