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:
invalid_yaml_warnings: 0
missing_app_warnings: 0
latitude: 30.250968
longitude: -97.748193
elevation: 150

View File

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

View File

@@ -1,15 +1,15 @@
import json
from copy import deepcopy
from appdaemon.plugins.hass.hassapi import Hass
from appdaemon.plugins.mqtt.mqttapi import Mqtt
class AqaraCube(Mqtt):
class AqaraCube(Hass, Mqtt):
def initialize(self):
self.set_namespace('mqtt')
topic = f'zigbee2mqtt/{self.args["cube"]}'
self.mqtt_subscribe(topic)
self.listen_event(self.handle_event, "MQTT_MESSAGE", topic=topic)
self.mqtt_subscribe(topic, namespace='mqtt')
self.listen_event(self.handle_event, "MQTT_MESSAGE", topic=topic, namespace='mqtt')
self.log(f'Listening for cube events on: {topic}')
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.log(f'Turned on {description}')
elif description == 'toggle':
cause = f'{action} from {self.args["cube"]}'
if self.app.entity_state:
self.app.deactivate(cause=cause)
else:
self.app.activate(cause=cause)
elif description.startswith('toggle'):
cause = f'{self.args["cube"]} {action}'
self.app.toggle_activate(kwargs={'cause': cause})
# def handle_rotate_right(self, payload):
# 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
class HelloWorld(Hass):
def initialize(self):
# self.set_state(entity_id='input_boolean.enable', state='on')
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):
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):
self.log(f'Changed state {old} -> {new}')
if old == 'home' and new != 'home':
self.turn_everything_off()
def turn_everything_off(self):
def turn_everything_off(self, entity, attribute, old, new, kwargs):
self.log(f'turning everything off')
self.log(kwargs)
for app_name in self.args['apps']:
try:
self.get_app(app_name).deactivate(cause='leaving')
self.get_app(app_name).deactivate(kwargs={'cause': 'leaving'})
except Exception as e:
self.log(f'{type(e).__name__}: {e}')
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'
scene:
light.bathroom:
brightness_pct: 40
brightness: 100
color_temp: 250
- time: '12:00:00'
scene:
light.bathroom:
brightness_pct: 70
brightness: 175
color_temp: 300
- time: sunset
scene:
light.bathroom:
brightness_pct: 50
brightness: 125
color_temp: 350
- time: '23:00:00'
scene:
light.bathroom:
brightness_pct: 20
brightness: 50
color_temp: 350
sleep: input_boolean.sleeping
sleep_state:
@@ -33,10 +33,10 @@ bathroom:
bathroom_button:
module: button
class: ButtonController
class: Button
app: bathroom
ref_entity: light.bathroom
button: Bathroom Button
ref_entity: light.bathroom
bathroom_motion:
module: motion

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
from appdaemon.plugins.hass.hassapi import Hass
class SceneDetector(Hass):
def initialize(self):
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):
super().scene_detected()
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):
def initialize(self):
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.log(f'{self.variable} can be set using {self.button}, currently {self.state}')
def setup_buttons(self):
if isinstance(self.button, list):
@@ -23,7 +22,7 @@ class SleepSetter(Hass, Mqtt):
topic = f'zigbee2mqtt/{name}'
self.mqtt_subscribe(topic, namespace='mqtt')
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
def button(self) -> str:
@@ -56,15 +55,13 @@ class SleepSetter(Hass, Mqtt):
@property
def sun_elevation(self) -> float:
try:
return float(self.get_state('sun.sun', 'elevation'))
except:
self.log(f'Failed to return sun elevation')
return
state = self.get_state('sun.sun', 'elevation')
assert isinstance(state, float)
return state
def handle_state(self, entity, attribute, old, new, kwargs):
self.log(f'{entity}: {old} -> {new}')
if self.state and self.sun_elevation < float(self.args['elevation_limit']):
self.log(f'new state: {self.state}')
if self.state:
self.all_off()
try:
self.call_service('scene/turn_on', entity_id=self.scene)
@@ -90,18 +87,14 @@ class SleepSetter(Hass, Mqtt):
def handle_action(self, action: str):
if action == '':
return
elif action == 'hold':
if action == 'hold':
self.log(f' {action.upper()} '.center(50, '='))
self.state = True
elif action == 'double':
self.log(f' {action.upper()} '.center(50, '='))
self.state = not self.state
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(cause='sleep setter')
except:
return
else:
self.log(f'Activated {app_name}')
self.on_apps()
def all_off(self):
self.log(f'Deactivating apps')
@@ -119,3 +112,13 @@ class SleepSetter(Hass, Mqtt):
except:
self.log(f'Failed to turn off {entity}')
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:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- config:/conf
- config:/conf:ro
ports:
- 5050:5050
restart: unless-stopped