Last active
July 8, 2018 03:14
-
-
Save ChadJPetersen/adc2193d097e12b32f28b61e99d74b7c to your computer and use it in GitHub Desktop.
A component for Home Assistant (home-assistant.io) that applies circadian changes to lights.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# An Home Assistant (home-assistant.io) service to component lights that accept RGB, XY or MIREDS color commands. | |
# v20170311 -- https://gist.github.com/xlcnd/01b2d8a28d9d77af901840072360a434 | |
# Original by 2017 Alexandre Conde <xlcnd@outlook.com> https://gist.github.com/xlcnd/01b2d8a28d9d77af901840072360a434 | |
# Edited by Chad Petersen July 2018 | |
# Licensed under MIT | |
# INSTALL: | |
# 1. Create the file <config dir>/custom_components/circadian_lights.py and copy the code below to there. | |
# 2. In your <config dir>/configuration.yaml enter (adapt to your case): | |
# circadian_lights: | |
# calmdown: '22:00:00' | |
# lights: | |
# - light.rgb_led_kitchen | |
# - light.rgb_led_tv | |
# - light.rgb_led_living_room | |
# - light.xy_led_toillet | |
# - light.temp_led_room1 | |
# - light.temp_led_room2 | |
# | |
# input_boolean: | |
# loop_circadian: | |
# name: Activate Circadian Lights | |
# initial: on | |
# icon: mdi:spotlight | |
# 3. and add to the automation section: | |
# - alias: 'Circadian Lights' | |
# trigger: | |
# platform: time | |
# minutes: '/5' | |
# seconds: 0 | |
# condition: | |
# - condition: state | |
# entity_id: 'input_boolean.loop_circadian' | |
# state: 'on' | |
# action: | |
# service: circadian_lights.change_color | |
# | |
# Restart HA and you will have the sun in your house and night lights 'friendly' of your circadian cycle. | |
# NOTE: The effect will only apply to lights that are on! | |
# \\HASSIO\config\custom_components\circadian_lights.py | |
import datetime | |
import logging | |
import voluptuous as vol | |
import homeassistant.helpers.config_validation as cv | |
from homeassistant.components import light | |
from homeassistant.helpers.sun import get_astral_event_next | |
from homeassistant.const import MATCH_ALL | |
from homeassistant.util.dt import as_utc, parse_time | |
# from homeassistant.util.color import color_temperature_to_rgb, color_RGB_to_xy#, HASS_COLOR_MAX, HASS_COLOR_MIN) | |
_LOGGER = logging.getLogger(__name__) | |
DOMAIN = 'circadian_lights' | |
DEPENDENCIES = ['light', 'sun'] | |
SERVICE_CHANGE_COLOR = 'change_color' | |
CONF_LIGHTS = 'lights' | |
CONF_CALMDOWN = 'calmdown' | |
DEFAULT_CALMDOWN = '23:00:00' # in local time | |
CONFIG_SCHEMA = vol.Schema({ | |
DOMAIN: vol.Schema({ | |
vol.Optional(CONF_CALMDOWN, default=DEFAULT_CALMDOWN): cv.string, | |
vol.Required(CONF_LIGHTS): cv.ensure_list, | |
}) | |
}, extra=vol.ALLOW_EXTRA) | |
def setup(hass, config): | |
"""Setup component circadian_lights.""" | |
# Read config | |
calm = config[DOMAIN].get(CONF_CALMDOWN, DEFAULT_CALMDOWN) | |
lights = config[DOMAIN].get(CONF_LIGHTS, MATCH_ALL) or [] | |
if not lights: | |
_LOGGER.error('Add light_ids to configuration under circadian_lights: %s', CONF_LIGHTS) | |
return False | |
def get_color(): | |
"""Sunlight colors while day, Red for night.""" | |
# See https://en.wikipedia.org/wiki/Color_temperature | |
# Get parameters | |
sunrise = as_utc(get_astral_event_next(hass, 'sunrise')) | |
sunset = as_utc(get_astral_event_next(hass, 'sunset')) | |
today = datetime.date.today() | |
now = as_utc(datetime.datetime.today()) | |
calmdown = as_utc(datetime.datetime.combine(today, parse_time(calm))) | |
# While is night | |
brightness = 0.5 # percentage | |
temperature = 1000 # in Kelvins (rgb=RED) | |
if sunrise > sunset: # While is day | |
sunrise = sunrise - datetime.timedelta(days=1) # aprox for sunrise today | |
midday = sunrise + datetime.timedelta(seconds=(sunset - sunrise).total_seconds() / 2) | |
if now < midday: # While is morning | |
temperature = 2000 + ((now - sunrise) / (midday - sunrise) * 4500) | |
brightness = brightness + ((now - sunrise) / (midday - sunrise) * (1 - brightness)) | |
else: # After midday, until sunset | |
temperature = 6500 - ((now - midday) / (sunset - midday) * 4500) | |
brightness = 1 - ((now - midday) / (sunset - midday) * (1 - brightness)) | |
else: # While is night but before calmdown | |
if now < calmdown and now.time() > sunrise.time(): # Before calmdown | |
sunset = sunset - datetime.timedelta(days=1) # aprox for sunset today | |
temperature = 2000 - ((calmdown - now) / (calmdown - sunset) * 1000) | |
return Color(temperature, brightness) | |
def get_active_lights(lights): | |
"""Get the ids of active lights.""" | |
ids = [] | |
for light_id in lights: | |
if hass.states.get(light_id) is None: | |
_LOGGER.error('Light id %s could not be found in state machine', light_id) | |
continue | |
if light.is_on(hass, light_id): | |
ids.append(light_id) | |
return ids | |
def change_color(): | |
"""Change color for each light (service).""" | |
light_ids = get_active_lights(lights) | |
if not light_ids: | |
return | |
color = get_color() | |
# _LOGGER.info('Temperature: %s mireds, %s kelvins, RGB: %s,%s,%s, XY: %s,%s, Brightness: %s', | |
# color.mireds, color.kelvins, color.r, color.g, color.b, | |
# color.x, color.y, color.brightness) | |
_LOGGER.info('Temperature: %s kelvins, Brightness: %s', | |
color.kelvins, color.brightness) | |
for light_id in light_ids: | |
if light.is_on(hass, light_id): | |
_LOGGER.info('Changing color of %s', light_id) | |
light.turn_on(hass, light_id, transition=3, | |
# rgb_color=[color.r, color.g, color.b], | |
# xy_color=[color.x, color.y], | |
# color_temp=color.mireds, | |
kelvin=color.kelvins, | |
brightness=color.brightness) | |
# Register service | |
hass.services.register(DOMAIN, SERVICE_CHANGE_COLOR, lambda call: change_color()) | |
return True | |
# # pylint: disable=too-few-public-methods | |
# # pylint: disable=too-many-instance-attributes | |
class Color(object): | |
"""Attributes for several color models.""" | |
def __init__(self, kelvins, brightness): | |
self.kelvins = kelvins | |
# self.mireds = min(500, max(154 , int(1000000 / kelvins))) | |
# self.mireds = min(HASS_COLOR_MAX, max(HASS_COLOR_MIN, int(1000000 / kelvins))) | |
# self.r, self.g, self.b = (min(int(c), 255) for c in color_temperature_to_rgb(kelvins)) | |
# self.x, self.y = color_RGB_to_xy(self.r, self.g, self.b) # v0.20 -- keep brightness | |
self.brightness = int(min(brightness * 255, 255)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment