pre-merge (kinda fucked up before)

This commit is contained in:
John Lancaster
2024-01-28 09:04:54 -06:00
parent dee31fe6f1
commit af8d13bfce
15 changed files with 162 additions and 102 deletions

View File

@@ -1,6 +1,4 @@
appdaemon: appdaemon:
invalid_yaml_warnings: 0
missing_app_warnings: 0
latitude: 30.250968 latitude: 30.250968
longitude: -97.748193 longitude: -97.748193
elevation: 150 elevation: 150

View File

@@ -13,9 +13,9 @@ scene_detect:
module: scene_detect module: scene_detect
class: MotionCanceller class: MotionCanceller
scene: bedsport scene: bedsport
app: bedroom app: bedroom_motion
scene_detect: scene_detect2:
module: scene_detect module: scene_detect
class: MotionCanceller class: MotionCanceller
scene: in_bed scene: in_bed

View File

@@ -1,15 +1,15 @@
import json import json
from copy import deepcopy from copy import deepcopy
from appdaemon.plugins.hass.hassapi import Hass
from appdaemon.plugins.mqtt.mqttapi import Mqtt from appdaemon.plugins.mqtt.mqttapi import Mqtt
class AqaraCube(Mqtt): class AqaraCube(Hass, Mqtt):
def initialize(self): def initialize(self):
self.set_namespace('mqtt')
topic = f'zigbee2mqtt/{self.args["cube"]}' topic = f'zigbee2mqtt/{self.args["cube"]}'
self.mqtt_subscribe(topic) self.mqtt_subscribe(topic, namespace='mqtt')
self.listen_event(self.handle_event, "MQTT_MESSAGE", topic=topic) self.listen_event(self.handle_event, "MQTT_MESSAGE", topic=topic, namespace='mqtt')
self.log(f'Listening for cube events on: {topic}') self.log(f'Listening for cube events on: {topic}')
self.app = self.get_app(self.args['app']) self.app = self.get_app(self.args['app'])
@@ -39,12 +39,9 @@ class AqaraCube(Mqtt):
self.call_service('scene/turn_on', entity_id=description, namespace='default') self.call_service('scene/turn_on', entity_id=description, namespace='default')
self.log(f'Turned on {description}') self.log(f'Turned on {description}')
elif description == 'toggle': elif description.startswith('toggle'):
cause = f'{action} from {self.args["cube"]}' cause = f'{self.args["cube"]} {action}'
if self.app.entity_state: self.app.toggle_activate(kwargs={'cause': cause})
self.app.deactivate(cause=cause)
else:
self.app.activate(cause=cause)
# def handle_rotate_right(self, payload): # def handle_rotate_right(self, payload):
# self.log(f'{self.args["cube"]}: Rotate right') # self.log(f'{self.args["cube"]}: Rotate right')

View File

@@ -1,7 +1,29 @@
from datetime import datetime
from appdaemon.plugins.hass.hassapi import Hass from appdaemon.plugins.hass.hassapi import Hass
class HelloWorld(Hass): class HelloWorld(Hass):
def initialize(self): def initialize(self):
# self.set_state(entity_id='input_boolean.enable', state='on')
self.log('Hello World') self.log('Hello World')
now: datetime = self.get_now()
eid = self.args['eid']
self.log(eid)
self.listen_state(self.my_callback, eid)
self.set_state(eid, state=now.isoformat(), attributes=dict(name='Test Fade Start'))
self.log(f'Set state to {now.time()}')
def my_callback(self, entity, attribute, old, new, kwargs):
new = self.convert_time(new)
old = self.convert_time(old)
self.log(f'{attribute} {old} -> {new}')
def convert_time(self, time_str: str) -> datetime:
dt = datetime.fromisoformat(time_str)
try:
dt = self.AD.tz.localize(dt)
except ValueError:
dt = dt.astimezone(self.AD.tz)
finally:
return dt

View File

@@ -1,18 +1,16 @@
from appdaemon.adapi import ADAPI from appdaemon.plugins.hass.hassapi import Hass
class Leaving(ADAPI):
class Leaving(Hass):
def initialize(self): def initialize(self):
self.listen_state(self.handle_state_change, entity_id=self.args['person']) self.listen_state(self.turn_everything_off, entity_id=self.args['person'], old='home')
def handle_state_change(self, entity, attribute, old, new, kwargs): def turn_everything_off(self, entity, attribute, old, new, kwargs):
self.log(f'Changed state {old} -> {new}') self.log(f'turning everything off')
if old == 'home' and new != 'home': self.log(kwargs)
self.turn_everything_off()
def turn_everything_off(self):
for app_name in self.args['apps']: for app_name in self.args['apps']:
try: try:
self.get_app(app_name).deactivate(cause='leaving') self.get_app(app_name).deactivate(kwargs={'cause': 'leaving'})
except Exception as e: except Exception as e:
self.log(f'{type(e).__name__}: {e}') self.log(f'{type(e).__name__}: {e}')
continue continue

33
apps/rich_logging.py Normal file
View File

@@ -0,0 +1,33 @@
import logging
from rich.console import Console
from rich.highlighter import NullHighlighter
from rich.logging import RichHandler
def init_logging(log_level: int = logging.INFO):
rich_handler = RichHandler(
console=Console(width=150),
highlighter=NullHighlighter(),
markup=True,
rich_tracebacks=True,
tracebacks_suppress=['pandas', 'discord'],
)
dt_fmt = '%Y-%m-%d %I:%M:%S %p'
# https://docs.python.org/3/library/logging.html#logrecord-attributes
log_format = '[magenta]%(name)s[/]: [cyan]%(funcName)s[/] %(message)s'
root_logger = logging.getLogger()
formatter = logging.Formatter(log_format)
formatter.datefmt = dt_fmt
rich_handler.setFormatter(formatter)
root_logger.addHandler(rich_handler)
root_logger.setLevel(log_level)
# logging.debug(f'Set up logging')
# logging.basicConfig(
# level=log_level,
# format=log_format,
# datefmt=dt_fmt,
# handlers=[rich_handler]
# )

View File

@@ -6,22 +6,22 @@ bathroom:
- time: '05:00:00' - time: '05:00:00'
scene: scene:
light.bathroom: light.bathroom:
brightness_pct: 40 brightness: 100
color_temp: 250 color_temp: 250
- time: '12:00:00' - time: '12:00:00'
scene: scene:
light.bathroom: light.bathroom:
brightness_pct: 70 brightness: 175
color_temp: 300 color_temp: 300
- time: sunset - time: sunset
scene: scene:
light.bathroom: light.bathroom:
brightness_pct: 50 brightness: 125
color_temp: 350 color_temp: 350
- time: '23:00:00' - time: '23:00:00'
scene: scene:
light.bathroom: light.bathroom:
brightness_pct: 20 brightness: 50
color_temp: 350 color_temp: 350
sleep: input_boolean.sleeping sleep: input_boolean.sleeping
sleep_state: sleep_state:
@@ -33,10 +33,10 @@ bathroom:
bathroom_button: bathroom_button:
module: button module: button
class: ButtonController class: Button
app: bathroom app: bathroom
ref_entity: light.bathroom
button: Bathroom Button button: Bathroom Button
ref_entity: light.bathroom
bathroom_motion: bathroom_motion:
module: motion module: motion

View File

@@ -8,11 +8,11 @@ bedroom:
light.bedroom: light.bedroom:
state: on state: on
color_temp: 200 color_temp: 200
brightness_pct: 20 brightness: 50
light.globe: light.globe:
state: on state: on
color_temp: 200 color_temp: 200
brightness_pct: 20 brightness: 50
light.overhead: light.overhead:
state: off state: off
- time: '06:00:00' - time: '06:00:00'
@@ -20,62 +20,62 @@ bedroom:
light.bedroom: light.bedroom:
state: on state: on
color_temp: 250 color_temp: 250
brightness_pct: 20 brightness: 50
light.globe: light.globe:
state: on state: on
color_temp: 250 color_temp: 250
brightness_pct: 20 brightness: 50
light.overhead: light.overhead:
state: on state: on
color_temp: 250 color_temp: 250
brightness_pct: 15 brightness: 40
- time: '12:00:00' - time: '12:00:00'
scene: scene:
light.bedroom: light.bedroom:
state: on state: on
color_temp: 325 color_temp: 325
brightness_pct: 30 brightness: 75
light.globe: light.globe:
state: on state: on
color_temp: 325 color_temp: 325
brightness_pct: 30 brightness: 75
light.overhead: light.overhead:
state: on state: on
color_temp: 325 color_temp: 325
brightness_pct: 50 brightness: 50
- time: 'sunset' - time: 'sunset'
scene: scene:
light.bedroom: light.bedroom:
state: on state: on
color_temp: 325 color_temp: 325
brightness_pct: 50 brightness: 50
light.globe: light.globe:
state: on state: on
color_temp: 325 color_temp: 325
brightness_pct: 50 brightness: 50
light.overhead: light.overhead:
state: on state: on
color_temp: 350 color_temp: 350
brightness_pct: 10 brightness: 65
- time: '01:00:00' - time: '01:00:00'
scene: scene:
light.bedroom: light.bedroom:
state: on state: on
color_name: green color_name: green
brightness_pct: 50 brightness: 50
light.globe: light.globe:
state: on state: on
color_name: blue color_name: blue
brightness_pct: 50 brightness: 50
light.overhead: light.overhead:
state: on state: on
color_name: blueviolet color_name: blueviolet
brightness_pct: 100 brightness: 255
sleep: input_boolean.sleeping sleep: input_boolean.sleeping
bedroom_buttons: bedroom_buttons:
module: button module: button
class: ButtonController class: Button
app: bedroom app: bedroom
ref_entity: light.bedroom ref_entity: light.bedroom
button: button:

View File

@@ -6,22 +6,22 @@ closet:
- time: 'sunrise - 03:00:00' - time: 'sunrise - 03:00:00'
scene: scene:
light.closet: light.closet:
brightness_pct: 10 brightness: 25
color_temp: 200 color_temp: 200
- time: 'sunrise' - time: 'sunrise'
scene: scene:
light.closet: light.closet:
brightness_pct: 30 brightness: 75
color_temp: 200 color_temp: 200
- time: '12:00:00' - time: '12:00:00'
scene: scene:
light.closet: light.closet:
brightness_pct: 70 brightness: 175
color_temp: 300 color_temp: 300
- time: sunset - time: sunset
scene: scene:
light.closet: light.closet:
brightness_pct: 40 brightness: 100
color_temp: 400 color_temp: 400
closet_motion: closet_motion:

View File

@@ -2,46 +2,46 @@ kitchen:
module: room_control module: room_control
class: RoomController class: RoomController
off_duration: '00:10:00' off_duration: '00:10:00'
ha_button: input_button.activate_kitchen # ha_button: input_button.activate_kitchen
sleep: input_boolean.sleeping
sleep_state:
scene:
light.kitchen:
state: on
brightness: 1
states: states:
- time: sunrise - time: sunrise
scene: scene:
light.kitchen: light.kitchen:
state: on state: on
color_temp: 200 color_temp: 200
brightness_pct: 10 brightness: 25
- 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: 75
- time: sunset - time: sunset
scene: scene:
light.kitchen: light.kitchen:
state: on state: on
color_temp: 450 color_temp: 450
brightness_pct: 40 brightness: 100
- 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: 25
sleep: input_boolean.sleeping
sleep_state:
scene:
light.kitchen:
state: on
brightness_pct: 1
kitchen_button: kitchen_button:
module: button module: button
class: ButtonController class: Button
app: kitchen app: kitchen
ref_entity: light.kitchen
button: Kitchen Button button: Kitchen Button
ref_entity: light.kitchen
kitchen_motion: kitchen_motion:
module: motion module: motion

View File

@@ -8,42 +8,42 @@ living_room:
light.living_room: light.living_room:
state: on state: on
color_temp: 200 color_temp: 200
brightness_pct: 30 brightness: 75
light.couch_corner: light.couch_corner:
state: on state: on
color_temp: 200 color_temp: 200
brightness_pct: 7 brightness: 20
- time: '09:00:00' - time: '09:00:00'
scene: scene:
light.living_room: light.living_room:
state: on state: on
color_temp: 250 color_temp: 250
brightness_pct: 50 brightness: 130
light.couch_corner: light.couch_corner:
state: on state: on
color_temp: 250 color_temp: 250
brightness_pct: 25 brightness: 65
- time: '12:00:00' - time: '12:00:00'
scene: scene:
light.living_room: light.living_room:
state: on state: on
color_temp: 300 color_temp: 300
brightness_pct: 100 brightness: 255
light.couch_corner: light.couch_corner:
state: on state: on
color_temp: 450 color_temp: 450
brightness_pct: 50 brightness: 125
- time: sunset - time: sunset
off_duration: 01:00:00 off_duration: 01:00:00
scene: scene:
light.living_room: light.living_room:
state: on state: on
color_temp: 350 color_temp: 350
brightness_pct: 70 brightness: 175
light.couch_corner: light.couch_corner:
state: on state: on
color_temp: 650 color_temp: 650
brightness_pct: 10 brightness: 25
- elevation: -20 - elevation: -20
direction: setting direction: setting
off_duration: 00:30:00 off_duration: 00:30:00
@@ -51,29 +51,29 @@ living_room:
light.living_room: light.living_room:
state: on state: on
color_temp: 350 color_temp: 350
brightness_pct: 50 brightness: 125
light.couch_corner: light.couch_corner:
state: on state: on
color_temp: 650 color_temp: 650
brightness_pct: 5 brightness: 5
sleep: input_boolean.sleeping sleep: input_boolean.sleeping
sleep_state: sleep_state:
off_duration: '00:02:00' # off_duration: '00:02:00'
scene: scene:
light.living_room: light.living_room:
state: 'on' state: 'on'
color_name: 'red' rgb_color: [255, 0, 0]
brightness_pct: 10 brightness: 25
front_door: front_door:
module: door module: door
class: DoorControl class: Door
app: living_room app: living_room
door: binary_sensor.front_contact door: binary_sensor.front_contact
living_room_button: living_room_button:
module: button module: button
class: ButtonController class: Button
app: living_room app: living_room
button: Living Room Button button: Living Room Button
ref_entity: light.living_room ref_entity: light.living_room

View File

@@ -1,5 +1,6 @@
from appdaemon.plugins.hass.hassapi import Hass from appdaemon.plugins.hass.hassapi import Hass
class SceneDetector(Hass): class SceneDetector(Hass):
def initialize(self): def initialize(self):
self.scene_entity = self.args['scene'] if self.args['scene'].startswith('scene.') else f'scene.{self.args["scene"]}' self.scene_entity = self.args['scene'] if self.args['scene'].startswith('scene.') else f'scene.{self.args["scene"]}'
@@ -21,4 +22,12 @@ class MotionCanceller(SceneDetector):
def scene_detected(self): def scene_detected(self):
super().scene_detected() super().scene_detected()
app = self.get_app(self.args['app']) app = self.get_app(self.args['app'])
app.cancel_motion_callback(new='off') try:
self.run_in(
callback=lambda *args, **kwargs: app.cancel_motion_callback(),
delay=0.5
)
except:
self.log(f'Error cancelling motion callback for {self.args["app"]}', level='ERROR')
else:
self.log('Cancelled motion callback')

View File

@@ -8,9 +8,8 @@ from appdaemon.plugins.mqtt.mqttapi import Mqtt
class SleepSetter(Hass, Mqtt): class SleepSetter(Hass, Mqtt):
def initialize(self): def initialize(self):
assert self.entity_exists(entity_id=self.variable), f'{self.variable} does not exist' assert self.entity_exists(entity_id=self.variable), f'{self.variable} does not exist'
self.variable_entity.listen_state(callback=self.handle_state) self.listen_state(callback=self.handle_state, entity_id=self.variable)
self.setup_buttons() self.setup_buttons()
self.log(f'{self.variable} can be set using {self.button}, currently {self.state}')
def setup_buttons(self): def setup_buttons(self):
if isinstance(self.button, list): if isinstance(self.button, list):
@@ -23,7 +22,7 @@ class SleepSetter(Hass, Mqtt):
topic = f'zigbee2mqtt/{name}' topic = f'zigbee2mqtt/{name}'
self.mqtt_subscribe(topic, namespace='mqtt') self.mqtt_subscribe(topic, namespace='mqtt')
self.listen_event(self.handle_button, "MQTT_MESSAGE", topic=topic, namespace='mqtt', button=name) self.listen_event(self.handle_button, "MQTT_MESSAGE", topic=topic, namespace='mqtt', button=name)
self.log(f'Listening for sleep setting on {name}') self.log(f'Subscribed: {topic}')
@property @property
def button(self) -> str: def button(self) -> str:
@@ -56,15 +55,13 @@ class SleepSetter(Hass, Mqtt):
@property @property
def sun_elevation(self) -> float: def sun_elevation(self) -> float:
try: state = self.get_state('sun.sun', 'elevation')
return float(self.get_state('sun.sun', 'elevation')) assert isinstance(state, float)
except: return state
self.log(f'Failed to return sun elevation')
return
def handle_state(self, entity, attribute, old, new, kwargs): def handle_state(self, entity, attribute, old, new, kwargs):
self.log(f'{entity}: {old} -> {new}') self.log(f'new state: {self.state}')
if self.state and self.sun_elevation < float(self.args['elevation_limit']): if self.state:
self.all_off() self.all_off()
try: try:
self.call_service('scene/turn_on', entity_id=self.scene) self.call_service('scene/turn_on', entity_id=self.scene)
@@ -90,18 +87,14 @@ class SleepSetter(Hass, Mqtt):
def handle_action(self, action: str): def handle_action(self, action: str):
if action == '': if action == '':
return return
elif action == 'hold':
if action == 'hold':
self.log(f' {action.upper()} '.center(50, '='))
self.state = True self.state = True
elif action == 'double': elif action == 'double':
self.log(f' {action.upper()} '.center(50, '='))
self.state = not self.state self.state = not self.state
if (on_apps := self.args.get('on_apps', None)) is not None: self.on_apps()
for app_name in on_apps:
try:
self.get_app(app_name).activate(cause='sleep setter')
except:
return
else:
self.log(f'Activated {app_name}')
def all_off(self): def all_off(self):
self.log(f'Deactivating apps') self.log(f'Deactivating apps')
@@ -119,3 +112,13 @@ class SleepSetter(Hass, Mqtt):
except: except:
self.log(f'Failed to turn off {entity}') self.log(f'Failed to turn off {entity}')
continue continue
def on_apps(self):
if (on_apps := self.args.get('on_apps', None)) is not None:
for app_name in on_apps:
try:
self.get_app(app_name).activate(kwargs={'cause': 'sleep setter'})
except:
return
else:
self.log(f'Activated {app_name}')

View File

@@ -6,7 +6,7 @@ services:
volumes: volumes:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro - /etc/timezone:/etc/timezone:ro
- config:/conf - config:/conf:ro
ports: ports:
- 5050:5050 - 5050:5050
restart: unless-stopped restart: unless-stopped