From 5ed565789333e16dd82e13d640e3dfa614f8b6c4 Mon Sep 17 00:00:00 2001 From: Nick Davies Date: Thu, 10 Mar 2022 11:45:10 -0700 Subject: [PATCH 1/3] Add .gitignore to ignore __pycache__ I have this repo as a submodule in my hass-config repo and the pycache directory makes it unclean which prevents ansible from automatically updating the repo --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ From 19df0f8a616cdd056981a20453f624a85884d86d Mon Sep 17 00:00:00 2001 From: Clayton Nummer Date: Sat, 7 May 2022 20:47:31 -0400 Subject: [PATCH 2/3] Make __init__ async --- .../circadian_lighting/__init__.py | 105 +++++++++--------- .../circadian_lighting/sensor.py | 22 ++-- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/custom_components/circadian_lighting/__init__.py b/custom_components/circadian_lighting/__init__.py index 21ae281..8fe454d 100644 --- a/custom_components/circadian_lighting/__init__.py +++ b/custom_components/circadian_lighting/__init__.py @@ -27,8 +27,8 @@ lights to 2700K (warm white) until your hub goes into Night mode """ +import asyncio import bisect -import logging from datetime import timedelta import voluptuous as vol @@ -43,7 +43,7 @@ SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, ) -from homeassistant.helpers.discovery import load_platform +from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import ( async_track_sunrise, @@ -51,14 +51,12 @@ async_track_time_change, async_track_time_interval, ) +from homeassistant.helpers.sun import get_astral_location from homeassistant.util.color import ( color_RGB_to_xy, color_temperature_to_rgb, color_xy_to_hs, ) -from homeassistant.helpers.sun import get_astral_location - -_LOGGER = logging.getLogger(__name__) DOMAIN = "circadian_lighting" CIRCADIAN_LIGHTING_UPDATE_TOPIC = f"{DOMAIN}_update" @@ -102,7 +100,7 @@ ) -def setup(hass, config): +async def async_setup(hass, config) -> bool: """Set up the Circadian Lighting platform.""" conf = config[DOMAIN] hass.data[DOMAIN] = CircadianLighting( @@ -116,10 +114,12 @@ def setup(hass, config): latitude=conf.get(CONF_LATITUDE, hass.config.latitude), longitude=conf.get(CONF_LONGITUDE, hass.config.longitude), elevation=conf.get(CONF_ELEVATION, hass.config.elevation), - interval=conf.get(CONF_INTERVAL), transition=conf.get(ATTR_TRANSITION), ) - load_platform(hass, "sensor", DOMAIN, {}, config) + await hass.data[DOMAIN]._async_init(interval=conf.get(CONF_INTERVAL)) + hass.async_create_task( + async_load_platform(hass, "sensor", DOMAIN, {}, config) + ) return True @@ -139,7 +139,6 @@ def __init__( latitude, longitude, elevation, - interval, transition, ): self.hass = hass @@ -154,53 +153,53 @@ def __init__( self._elevation = elevation self._transition = transition - self._percent = self.calc_percent() - self._colortemp = self.calc_colortemp() - self._rgb_color = self.calc_rgb() - self._xy_color = self.calc_xy() - self._hs_color = self.calc_hs() + async def _async_init(self, interval): + self._percent = await self.async_calc_percent() + self._colortemp = await self.async_calc_colortemp() + self._rgb_color = await self.async_calc_rgb() + self._xy_color = await self.async_calc_xy() if self._manual_sunrise is not None: async_track_time_change( self.hass, - self.update, + self.async_update, hour=self._manual_sunrise.hour, minute=self._manual_sunrise.minute, second=self._manual_sunrise.second, ) else: - async_track_sunrise(self.hass, self.update, self._sunrise_offset) + async_track_sunrise(self.hass, self.async_update, self._sunrise_offset) if self._manual_sunset is not None: async_track_time_change( self.hass, - self.update, + self.async_update, hour=self._manual_sunset.hour, minute=self._manual_sunset.minute, second=self._manual_sunset.second, ) else: - async_track_sunset(self.hass, self.update, self._sunset_offset) + async_track_sunset(self.hass, self.async_update, self._sunset_offset) - async_track_time_interval(self.hass, self.update, interval) + async_track_time_interval(self.hass, self.async_update, interval) - def _replace_time(self, date, key): + async def _async_replace_time(self, date, key): other_date = self._manual_sunrise if key == "sunrise" else self._manual_sunset - return date.replace( + return await self.hass.async_add_executor_job(date.replace, hour=other_date.hour, minute=other_date.minute, second=other_date.second, microsecond=other_date.microsecond, ) - def _get_sun_events(self, date): + async def _async_get_sun_events(self, date): if self._manual_sunrise is not None and self._manual_sunset is not None: - sunrise = self._replace_time(date, "sunrise") - sunset = self._replace_time(date, "sunset") + sunrise = await self._async_replace_time(date, "sunrise") + sunset = await self._async_replace_time(date, "sunset") solar_noon = sunrise + (sunset - sunrise) / 2 solar_midnight = sunset + ((sunrise + timedelta(days=1)) - sunset) / 2 else: - _loc = get_astral_location(self.hass) + _loc = await self.hass.async_add_executor_job(get_astral_location, self.hass) if isinstance(_loc, tuple): # Astral v2.2 location, _ = _loc @@ -214,23 +213,23 @@ def _get_sun_events(self, date): location.elevation = self._elevation if self._manual_sunrise is not None: - sunrise = self._replace_time(date, "sunrise") + sunrise = await self._async_replace_time(date, "sunrise") else: - sunrise = location.sunrise(date) + sunrise = await self.hass.async_add_executor_job(location.sunrise, date) if self._manual_sunset is not None: - sunset = self._replace_time(date, "sunset") + sunset = await self._async_replace_time(date, "sunset") else: - sunset = location.sunset(date) + sunset = await self.hass.async_add_executor_job(location.sunset, date) try: - solar_noon = location.noon(date) + solar_noon = await self.hass.async_add_executor_job(location.noon, date) except AttributeError: - solar_noon = location.solar_noon(date) + solar_noon = await self.hass.async_add_executor_job(location.solar_noon, date) try: - solar_midnight = location.midnight(date) + solar_midnight = await self.hass.async_add_executor_job(location.midnight, date) except AttributeError: - solar_midnight = location.solar_midnight(date) + solar_midnight = await self.hass.async_add_executor_job(location.solar_midnight, date) if self._sunrise_offset is not None: sunrise = sunrise + self._sunrise_offset @@ -248,19 +247,19 @@ def _get_sun_events(self, date): k: dt.astimezone(dt_util.UTC).timestamp() for k, dt in datetimes.items() } - def _relevant_events(self, now): + async def _async_relevant_events(self, now): events = [] for days in [-1, 0, 1]: - sun_events = self._get_sun_events(now + timedelta(days=days)) + sun_events = await self._async_get_sun_events(now + timedelta(days=days)) events.extend(list(sun_events.items())) events = sorted(events, key=lambda x: x[1]) index_now = bisect.bisect([ts for _, ts in events], now.timestamp()) return dict(events[index_now - 2 : index_now + 2]) - def calc_percent(self): - now = dt_util.utcnow() - now_ts = now.timestamp() - today = self._relevant_events(now) + async def async_calc_percent(self): + now = await self.hass.async_add_executor_job(dt_util.utcnow) + now_ts = await self.hass.async_add_executor_job(now.timestamp) + today = await self._async_relevant_events(now) # Figure out where we are in time so we know which half of the # parabola to calculate. We're generating a different # sunset-sunrise parabola for before and after solar midnight. @@ -294,7 +293,7 @@ def calc_percent(self): percentage = a * (now_ts - h) ** 2 + k return percentage - def calc_colortemp(self): + async def async_calc_colortemp(self): if self._percent > 0: delta = self._max_colortemp - self._min_colortemp percent = self._percent / 100 @@ -302,20 +301,22 @@ def calc_colortemp(self): else: return self._min_colortemp - def calc_rgb(self): - return color_temperature_to_rgb(self._colortemp) + async def async_calc_rgb(self): + return await self.hass.async_add_executor_job(color_temperature_to_rgb, self._colortemp) - def calc_xy(self): - return color_RGB_to_xy(*self.calc_rgb()) + async def async_calc_xy(self): + rgb = await self.async_calc_rgb() + return await self.hass.async_add_executor_job(color_RGB_to_xy, *rgb) - def calc_hs(self): - return color_xy_to_hs(*self.calc_xy()) + async def async_calc_hs(self): + xy = await self.async_calc_xy() + return await self.hass.async_add_executor_job(color_xy_to_hs, *xy) - async def update(self, _=None): + async def async_update(self, _=None): """Update Circadian Values.""" - self._percent = self.calc_percent() - self._colortemp = self.calc_colortemp() - self._rgb_color = self.calc_rgb() - self._xy_color = self.calc_xy() - self._hs_color = self.calc_hs() + self._percent = await self.async_calc_percent() + self._colortemp = await self.async_calc_colortemp() + self._rgb_color = await self.async_calc_rgb() + self._xy_color = await self.async_calc_xy() + self._hs_color = await self.async_calc_hs() async_dispatcher_send(self.hass, CIRCADIAN_LIGHTING_UPDATE_TOPIC) diff --git a/custom_components/circadian_lighting/sensor.py b/custom_components/circadian_lighting/sensor.py index 2453f5e..a016e20 100755 --- a/custom_components/circadian_lighting/sensor.py +++ b/custom_components/circadian_lighting/sensor.py @@ -2,7 +2,9 @@ Circadian Lighting Sensor for Home-Assistant. """ -from homeassistant.core import callback +from homeassistant.core import callback, HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity @@ -11,22 +13,24 @@ ICON = "mdi:theme-light-dark" -def setup_platform(hass, config, add_devices, discovery_info=None): +async def async_setup_platform( + hass: HomeAssistant, + config: ConfigType, + async_add_entities: AddEntitiesCallback, + discovery_info = None, +) -> None: """Set up the Circadian Lighting sensor.""" circadian_lighting = hass.data.get(DOMAIN) if circadian_lighting is not None: sensor = CircadianSensor(hass, circadian_lighting) - add_devices([sensor], True) + async_add_entities([sensor]) - def update(call=None): + async def async_update(call = None) -> None: """Update component.""" - circadian_lighting.update() + await circadian_lighting.async_update() service_name = "values_update" - hass.services.register(DOMAIN, service_name, update) - return True - else: - return False + hass.services.async_register(DOMAIN, service_name, async_update) class CircadianSensor(Entity): From 9dec1f12c7096ca1842d425a4ade58ea563b9f10 Mon Sep 17 00:00:00 2001 From: Clayton Nummer Date: Sat, 7 May 2022 20:53:49 -0400 Subject: [PATCH 3/3] Bump version --- custom_components/circadian_lighting/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/circadian_lighting/manifest.json b/custom_components/circadian_lighting/manifest.json index 307bef5..bcf3b95 100644 --- a/custom_components/circadian_lighting/manifest.json +++ b/custom_components/circadian_lighting/manifest.json @@ -1,7 +1,7 @@ { "domain": "circadian_lighting", "name": "Circadian Lighting", - "version": "2.0.8-beta", + "version": "2.1.0-beta", "documentation": "https://github.com/claytonjn/hass-circadian_lighting", "issue_tracker": "https://github.com/claytonjn/hass-circadian_lighting/issues", "dependencies": ["sensor","switch"],