controllers start, kind of working

This commit is contained in:
John Lancaster
2023-04-15 15:15:37 -05:00
parent e13fadc5cf
commit 6aa9154a78
3 changed files with 204 additions and 38 deletions

145
apps/controller.py Normal file
View File

@@ -0,0 +1,145 @@
from dataclasses import dataclass, field
from datetime import timedelta
from typing import List
from appdaemon.adapi import ADAPI
from appdaemon.entity import Entity
from appdaemon.plugins.hass.hassapi import Hass
@dataclass(init=False)
class ControllerBase(Hass):
entities: List[Entity]
def initialize(self):
# assign fields
for arg, val in self.args.items():
if arg not in ['class', 'module']:
setattr(self, arg, val)
self.log(f'Set {arg} to {val}')
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]
self.log(f'Initialized controller for {[e.friendly_name for e in self.entities]}')
@dataclass(init=False)
class ControllerRoom(ControllerBase):
def initialize(self):
super().initialize()
self.register_service(f'{self.name}/activate', self.activate)
self.register_service(f'{self.name}/deactivate', self.deactivate)
def activate(self, namespace: str = None, domain: str = None, service=None, kwargs=None):
for entity in self.entities:
self.log(f'Turning on {entity.name}')
entity.turn_on()
def deactivate(self, namespace: str = None, domain: str = None, service=None, kwargs=None):
for entity in self.entities:
self.log(f'Turning off {entity.name}')
entity.turn_off()
@property
def state(self) -> bool:
return any([e.get_state() == 'on' for e in self.entities])
@dataclass(init=False)
class ControllerMotion(ControllerBase):
room: ControllerRoom
off_duration: timedelta
def initialize(self):
super().initialize()
# self.log('Motion Controller init')
# convert room to App
self.room: ControllerRoom = self.get_app(self.room)
# convert off_duration
try:
hours, minutes, seconds = map(int, self.args['off_duration'].split(':'))
self.off_duration = timedelta(hours=hours, minutes=minutes, seconds=seconds)
except Exception:
self.off_duration = timedelta()
self.sync_state()
self.listen_state(self.sync_state, [e.entity_id for e in self.entities])
@property
def current_state(self) -> bool:
return any(e.get_state() == 'on' for e in self.entities)
def sync_state(self,
entity=None,
attribute=None,
old=None,
new=None,
kwargs=None):
self.log(f'Syncing state, current state: {self.current_state}')
if self.current_state:
self.room.activate()
self.listen_motion_off()
else:
self.room.deactivate()
self.listen_motion_on()
def listen_motion_on(self):
self.listen_state(
callback=self.callback_motion_on,
entity_id=[e.entity_id for e in self.entities],
new='on',
oneshot=True
)
self.log(f'Waiting for motion on {[e.friendly_name for e in self.entities]} to turn on room {self.room.name}')
def listen_motion_off(self):
self.listen_state(
callback=self.callback_motion_off,
entity_id=[e.entity_id for e in self.entities],
new='off',
duration=self.off_duration.total_seconds(),
oneshot=True
)
self.log(f'Waiting for motion off {[e.friendly_name for e in self.entities]} for {self.off_duration}')
def callback_motion_on(self, entity, attribute, old, new, kwargs):
self.log(f'Motion detected on {self.friendly_name(entity)}')
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}')
self.room.deactivate()
@dataclass(init=False)
class ControllerButton(Hass):
room: ControllerRoom
buttons: List[str]
def initialize(self):
self.buttons = self.args['buttons']
# convert room to App
self.room: ControllerRoom = self.get_app(self.args['room'])
for button in self.buttons:
self.listen_event(
self.callback_button,
event='deconz_event',
id=button,
)
self.log(f'Listening to presses on button ID={button}')
def callback_button(self, event_name, data, kwargs):
# single press
if data['event'] == 1002:
self.log(f"Single press: {data['id']}")
if self.room.state:
self.room.deactivate()
else:
self.room.activate()

21
apps/kitchen.yaml Normal file
View File

@@ -0,0 +1,21 @@
kitchen:
module: controller
class: ControllerRoom
entities:
- light.kitchen
kitchen_motion:
module: controller
class: ControllerMotion
room: kitchen
off_duration: 00:01:00
entities:
- binary_sensor.motion_kitchen
# - binary_sensor.motion_bedroom
kitchen_button:
module: controller
class: ControllerButton
room: kitchen
buttons:
- kitchen

View File

@@ -170,44 +170,44 @@ living_room:
color_name: 'red' color_name: 'red'
brightness_pct: 50 brightness_pct: 50
kitchen: # kitchen:
module: basic_motion # module: basic_motion
class: MotionLight # class: MotionLight
entity: light.kitchen # entity: light.kitchen
sensor: binary_sensor.motion_kitchen # sensor: binary_sensor.motion_kitchen
off_duration: '00:10:00' # off_duration: '00:10:00'
button: kitchen_switch # button: kitchen_switch
scene: # scene:
- time: sunrise # - time: sunrise
scene: # scene:
light.kitchen: # light.kitchen:
state: on # state: on
color_temp: 200 # color_temp: 200
brightness_pct: 10 # brightness_pct: 10
- time: '12:00:00' # - time: '12:00:00'
scene: # scene:
light.kitchen: # light.kitchen:
state: on # state: on
color_temp: 300 # color_temp: 300
brightness_pct: 30 # brightness_pct: 30
- time: sunset # - time: sunset
scene: # scene:
light.kitchen: # light.kitchen:
state: on # state: on
color_temp: 450 # color_temp: 450
brightness_pct: 40 # brightness_pct: 40
- time: '22:00:00' # - time: '22:00:00'
off_duration: '00:02:00' # off_duration: '00:02:00'
scene: # scene:
light.kitchen: # light.kitchen:
state: on # state: on
color_temp: 650 # color_temp: 650
brightness_pct: 10 # brightness_pct: 10
sleep: input_boolean.sleeping # sleep: input_boolean.sleeping
sleep_scene: # sleep_scene:
light.kitchen: # light.kitchen:
state: on # state: on
brightness_pct: 1 # brightness_pct: 1
# patio: # patio: