From a8e0b53c53c3f47f53ae0556f2a0dbad906c0b5a Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Tue, 27 Aug 2024 00:42:19 -0500 Subject: [PATCH] changes for use_dictionary_unpacking --- appdaemon.yaml | 2 +- apps/hello_world/weather.py | 131 ++++++++++++++++++++++++++++++++++++ apps/room_control | 2 +- apps/scene_detect.py | 1 + apps/sleep.py | 4 +- 5 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 apps/hello_world/weather.py diff --git a/appdaemon.yaml b/appdaemon.yaml index 19ef267..d0f653f 100755 --- a/appdaemon.yaml +++ b/appdaemon.yaml @@ -1,6 +1,6 @@ appdaemon: uvloop: True - # use_dictionary_unpacking: True + use_dictionary_unpacking: True # check_app_updates_profile: True import_method: expert diff --git a/apps/hello_world/weather.py b/apps/hello_world/weather.py new file mode 100644 index 0000000..162b467 --- /dev/null +++ b/apps/hello_world/weather.py @@ -0,0 +1,131 @@ +from datetime import timedelta +from pathlib import Path + +import aiohttp +import yaml +from appdaemon.plugins.hass.hassapi import Hass + + +def convert_vals_dict(vals): + weather_codes = { + 0: 'Unknown', + 1000: 'Clear', + 1001: 'Cloudy', + 1100: 'Mostly Clear', + 1101: 'Partly Cloudy', + 1102: 'Mostly Cloudy', + 2000: 'Fog', + 2100: 'Light Fog', + 3000: 'Light Wind', + 3001: 'Wind', + 3002: 'Strong Wind', + 4000: 'Drizzle', + 4001: 'Rain', + 4200: 'Light Rain', + 4201: 'Heavy Rain', + 5000: 'Snow', + 5001: 'Flurries', + 5100: 'Light Snow', + 5101: 'Heavy Snow', + 6000: 'Freezing Drizzle', + 6001: 'Freezing Rain', + 6200: 'Light Freezing Rain', + 6201: 'Heavy Freezing Rain', + 7000: 'Ice Pellets', + 7101: 'Heavy Ice Pellets', + 7102: 'Light Ice Pellets', + 8000: 'Thunderstorm', + } + + return { + 'cloud_coverage': int(round(vals['cloudCover'], 0)), + 'condition': weather_codes[vals['weatherCode']], + 'humidity': vals['humidity'], + 'native_apparent_temperature': vals['temperatureApparent'], + 'native_dew_point': vals['dewPoint'], + 'native_precipitation_unit': 'in.', + 'native_pressure': vals['pressureSurfaceLevel'], + 'native_pressure_unit': 'inHg', + 'native_temperature': vals['temperature'], + 'native_temperature_unit': 'F', + 'native_wind_gust_speed': vals['windGust'], + 'native_wind_speed': vals['windSpeed'], + 'native_wind_speed_unit': 'mph', + 'uv_index': vals['uvIndex'], + 'wind_bearing': vals['windDirection'], + } + + +class Weather(Hass): + def initialize(self): + with (Path(self.AD.config_dir) / 'secrets.yaml').open('r') as f: + apikey = yaml.safe_load(f)['tomorrow.io'] + self.log('API key loaded') + + lat = self.AD.sched.location.latitude + long = self.AD.sched.location.longitude + self.request_kwargs = { + 'url': 'https://api.tomorrow.io/v4/weather/forecast', + 'params': { + 'apikey': apikey, + 'location': f'{lat},{long}', + 'timesteps': '1h', + 'units': 'imperial', + }, + } + + if loc := self.args.get('location'): + self.request_kwargs['params']['location'] = loc + self.log(f'Updated location to {loc}', level='DEBUG') + + interval = timedelta(minutes=5) + self.run_every(self.get_weather_async, 'now', interval.total_seconds()) + self.log(f'Getting weather every {interval}') + + async def get_weather_async(self, **kwargs): + async with aiohttp.ClientSession() as session: + async with session.get(**self.request_kwargs) as resp: + if resp.status == 200: + self.log('Got weather async', level='DEBUG') + json_data = await resp.json() + await self.publish_temp(json_data) + elif resp.status == 429: + self.log('Rate limited when getting weather', level='WARNING') + else: + self.log(f'Error getting weather async: {resp.status}', level='ERROR') + + async def publish_temp(self, json_data): + vals = convert_vals_dict(json_data['timelines']['hourly'][0]['values']) + await self.set_state('weather.tomorrowio', state=vals['condition'], **vals) + + # def create_sensor(self, name: str, **kwargs) -> None: + # mqtt = self.app.get_plugin_api("MQTT") + + # if "friendly_name" in kwargs: + # friendly_name = kwargs["friendly_name"] + # del kwargs["friendly_name"] + # else: + # friendly_name = name + + # self.mqtt_registry[name] = {"entity_id": f"sensor.{name}"} + + # if "initial_value" in kwargs: + # # create the state topic first and set the value + # self.set_state(name, state=kwargs["initial_value"]) + # del kwargs["initial_value"] + + # config = { + # "name": friendly_name, + # "object_id": name, + # "state_topic": f"appdaemon/{name}/state", + # "value_template": "{{ value_json.state }}", + # } + + # for key, value in kwargs.items(): + # config[key] = value + + # mqtt.mqtt_publish( + # f"homeassistant/sensor/appdaemon/{name}/config", + # json.dumps(config), + # retain=True, + # ) diff --git a/apps/room_control b/apps/room_control index 0f58ac4..1b3fc4a 160000 --- a/apps/room_control +++ b/apps/room_control @@ -1 +1 @@ -Subproject commit 0f58ac4cc6816e2dc2711659eb5f2f934943dc22 +Subproject commit 1b3fc4afb7e118ebccd4e5702ad35947881f0b89 diff --git a/apps/scene_detect.py b/apps/scene_detect.py index b96edea..3c45708 100644 --- a/apps/scene_detect.py +++ b/apps/scene_detect.py @@ -41,6 +41,7 @@ class MotionCanceller(SceneDetector): for handle, info in callbacks.items(): if info['entity'] == app.motion.sensor_entity_id and 'new=off' in info['kwargs']: + self.cancel_listen_state(handle) await self.AD.state.cancel_state_callback(handle, app.name) self.log(f'Cancelled motion callback for {app.name}') # self.log(json.dumps(info, indent=4)) diff --git a/apps/sleep.py b/apps/sleep.py index 746f532..9162389 100755 --- a/apps/sleep.py +++ b/apps/sleep.py @@ -65,7 +65,7 @@ class SleepSetter(Hass, Mqtt): assert isinstance(state, float) return state - def handle_state(self, entity, attribute, old, new, kwargs): + def handle_state(self, entity, attribute, old, new, **kwargs): self.log(f'new state: {self.state}') if self.state: self.all_off() @@ -77,7 +77,7 @@ class SleepSetter(Hass, Mqtt): self.log(f'Turned on scene: {self.scene}') # self.turn_on(self.scene) - def handle_button(self, event_name, data, kwargs): + def handle_button(self, event_name, data, **kwargs): topic = data['topic'] # self.log(f'Button event for: {topic}') try: