From bc175f75d9a8a7e07259336a67d7cc9921be6770 Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Tue, 11 Jun 2024 23:03:59 -0500 Subject: [PATCH] improved scene detect --- .gitignore | 3 ++- apps/apps.yaml | 4 --- apps/hello.py | 29 ---------------------- apps/hello_world/hello.py | 49 +++++++++++++++++++++++++++++++++++++ apps/hello_world/hello.yaml | 3 +++ apps/scene_detect.py | 34 ++++++++++++++----------- 6 files changed, 74 insertions(+), 48 deletions(-) delete mode 100644 apps/hello.py create mode 100644 apps/hello_world/hello.py create mode 100644 apps/hello_world/hello.yaml diff --git a/.gitignore b/.gitignore index 40a3565..6db8a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ secrets.yaml *.ipynb -logs/ \ No newline at end of file +logs/ +*.json \ No newline at end of file diff --git a/apps/apps.yaml b/apps/apps.yaml index 2d4cd92..eee1644 100644 --- a/apps/apps.yaml +++ b/apps/apps.yaml @@ -1,7 +1,3 @@ -hello_world: - module: hello_world - class: HelloWorld - rich_logging: module: console global: true diff --git a/apps/hello.py b/apps/hello.py deleted file mode 100644 index 8035937..0000000 --- a/apps/hello.py +++ /dev/null @@ -1,29 +0,0 @@ -from datetime import datetime - -from appdaemon.plugins.hass.hassapi import Hass - - -class HelloWorld(Hass): - def initialize(self): - 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 \ No newline at end of file diff --git a/apps/hello_world/hello.py b/apps/hello_world/hello.py new file mode 100644 index 0000000..a4f604c --- /dev/null +++ b/apps/hello_world/hello.py @@ -0,0 +1,49 @@ +import asyncio +import json + +from appdaemon.adapi import ADAPI +from appdaemon.plugins.hass.hassapi import Hass + + +class HelloWorld(Hass): + def initialize(self): + self.log('Hello World') + # self.listen_state( + # callback=self.temp_callback, + # entity_id='sensor.temperature_nest', + # attribute='state', + # threshold=65.0, + # ) + # self.listen_state( + # callback=self.temp_callback, + # entity_id='light.living_room', + # attribute='state', + # threshold=65.0, + # ) + + async def temp_callback(self, entity, attribute, old, new, kwargs): + self.log('Temp callback') + temp = await self.get_state('sensor.temperature_nest') + # self.log(json.dumps(temp, indent=4)) + self.AD.loop.create_task(self.unreliable_call()) + if float(temp) <= kwargs['threshold']: + self.log(f'{entity} is below the threshold') + self.AD.loop.create_task(self.unreliable_call()) + + self.log('Doing some other, more reliable stuff') + await asyncio.sleep(2.0) + self.log(f'{entity} done') + + async def unreliable_call(self): + self.log('Calling unreliable cloud service....') + await asyncio.sleep(5.0) + # await self.call_service('climate/set_temperature', entity_id='climate.living_room', temperature=70) + self.log('Cloud service returned') + + def entities_ending_with(self, ending_str: str): + entities = [ + entity_id + for entity_id, state in self.get_state().items() + if entity_id.endswith(ending_str) + ] + return entities diff --git a/apps/hello_world/hello.yaml b/apps/hello_world/hello.yaml new file mode 100644 index 0000000..d2ed8db --- /dev/null +++ b/apps/hello_world/hello.yaml @@ -0,0 +1,3 @@ +HelloWorld: + module: hello + class: HelloWorld \ No newline at end of file diff --git a/apps/scene_detect.py b/apps/scene_detect.py index c06fdc3..2927835 100644 --- a/apps/scene_detect.py +++ b/apps/scene_detect.py @@ -1,4 +1,8 @@ +import json +import re + from appdaemon.plugins.hass.hassapi import Hass +from room_control import Motion class SceneDetector(Hass): @@ -15,25 +19,27 @@ class SceneDetector(Hass): ) self.log(f'Waiting for {self.scene_entity.friendly_name} to activate') - def event_callback(self, event_name, data, cb_args): + async def event_callback(self, event_name, data, cb_args): entity_id = data['service_data']['entity_id'] if entity_id == self.scene_entity.entity_id: - self.scene_detected() + await self.scene_detected() - def scene_detected(self): + async def scene_detected(self): self.log(f'Detected scene activation: {self.scene_entity.friendly_name}') class MotionCanceller(SceneDetector): - def scene_detected(self): - super().scene_detected() - app = self.get_app(self.args['app']) - try: - self.run_in(callback=lambda *args, **kwargs: app.cancel_motion_callback(), delay=0.5) - except Exception: - self.log( - f'Error cancelling motion callback for {self.args["app"]}', - level='ERROR', - ) + async def scene_detected(self): + await super().scene_detected() + app: Motion = await self.get_app(self.args['app']) + all_callbacks = await self.get_callback_entries() + app_callbacks = all_callbacks[app.name] + + for handle, info in app_callbacks.items(): + if info['entity'] == app.sensor.entity_id and 'new=off' in info['kwargs']: + self.log(f'Cancelling motion callback for {app.name}') + await self.AD.state.cancel_state_callback(handle, app.name) + # self.log(json.dumps(info, indent=4)) + break else: - self.log('Cancelled motion callback') + self.log('Did not cancel anything', level='WARNING')