Skip to content

Commit

Permalink
Make __init__ async
Browse files Browse the repository at this point in the history
  • Loading branch information
claytonjn committed May 8, 2022
1 parent 17ed486 commit 19df0f8
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 61 deletions.
105 changes: 53 additions & 52 deletions custom_components/circadian_lighting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,22 +43,20 @@
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,
async_track_sunset,
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"
Expand Down Expand Up @@ -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(
Expand All @@ -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

Expand All @@ -139,7 +139,6 @@ def __init__(
latitude,
longitude,
elevation,
interval,
transition,
):
self.hass = hass
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -294,28 +293,30 @@ 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
return (delta * percent) + self._min_colortemp
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)
22 changes: 13 additions & 9 deletions custom_components/circadian_lighting/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand Down

0 comments on commit 19df0f8

Please sign in to comment.