Skip to content

Instantly share code, notes, and snippets.

@ChadJPetersen
Last active July 8, 2018 03:14
Show Gist options
  • Save ChadJPetersen/adc2193d097e12b32f28b61e99d74b7c to your computer and use it in GitHub Desktop.
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.
# 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