17 changed files with 986 additions and 4 deletions
-
5.env-services-dist
-
9.gitignore
-
3README.md
-
22docker-compose.yml
-
1etc/config/automations.yaml
-
50etc/config/blueprints/automation/homeassistant/motion_light.yaml
-
43etc/config/blueprints/automation/homeassistant/notify_leaving_zone.yaml
-
64etc/config/configuration.yaml
-
316etc/config/custom_components/circadian_lighting/__init__.py
-
8etc/config/custom_components/circadian_lighting/manifest.json
-
104etc/config/custom_components/circadian_lighting/sensor.py
-
2etc/config/custom_components/circadian_lighting/services.yaml
-
362etc/config/custom_components/circadian_lighting/switch.py
-
0etc/config/groups.yaml
-
1etc/config/recorder.yaml
-
0etc/config/scenes.yaml
-
0etc/config/scripts.yaml
@ -0,0 +1,5 @@ |
|||||
|
# homeassistant_db |
||||
|
MYSQL_DATABASE= |
||||
|
MYSQL_USER= |
||||
|
MYSQL_ROOT_PASSWORD= |
||||
|
MYSQL_PASSWORD= |
@ -0,0 +1,9 @@ |
|||||
|
/etc/config/.cloud |
||||
|
/etc/config/.storage |
||||
|
/etc/config/deps |
||||
|
/etc/config/tts |
||||
|
/etc/config/.HA_VERSION |
||||
|
/etc/config/home-assistant.log |
||||
|
/etc/config/secrets.yaml |
||||
|
/etc/homeassistant_db |
||||
|
/.env-homeassistant_db |
@ -1,20 +1,34 @@ |
|||||
version: "3" |
version: "3" |
||||
|
|
||||
services: |
services: |
||||
home-assistant: |
|
||||
|
homeassistant_instance: |
||||
image: homeassistant/home-assistant:2021.2.3 |
image: homeassistant/home-assistant:2021.2.3 |
||||
container_name: home-assistant |
|
||||
hostname: home-assistant |
|
||||
|
container_name: homeassistant_instance |
||||
|
hostname: homeassistant_instance |
||||
restart: always |
restart: always |
||||
|
depends_on: |
||||
|
- homeassistant_db |
||||
|
|
||||
logging: |
logging: |
||||
driver: json-file |
driver: json-file |
||||
options: |
options: |
||||
max-size: "10m" |
max-size: "10m" |
||||
max-file: "5" |
max-file: "5" |
||||
|
|
||||
volumes: |
volumes: |
||||
- /etc/localtime:/etc/localtime:ro |
- /etc/localtime:/etc/localtime:ro |
||||
- ./etc/config:/config |
- ./etc/config:/config |
||||
|
|
||||
ports: |
ports: |
||||
- "8123:8123" |
- "8123:8123" |
||||
|
homeassistant_db: |
||||
|
image: mariadb:10.5 |
||||
|
container_name: homeassistant_db |
||||
|
|
||||
|
hostname: homeassistant_db |
||||
|
restart: unless-stopped |
||||
|
|
||||
|
env_file: |
||||
|
- ./.env-homeassistant_db |
||||
|
|
||||
|
volumes: |
||||
|
- ./etc/homeassistant_db/data:/var/lib/mysql |
@ -0,0 +1 @@ |
|||||
|
[] |
@ -0,0 +1,50 @@ |
|||||
|
blueprint: |
||||
|
name: Motion-activated Light |
||||
|
description: Turn on a light when motion is detected. |
||||
|
domain: automation |
||||
|
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml |
||||
|
input: |
||||
|
motion_entity: |
||||
|
name: Motion Sensor |
||||
|
selector: |
||||
|
entity: |
||||
|
domain: binary_sensor |
||||
|
device_class: motion |
||||
|
light_target: |
||||
|
name: Light |
||||
|
selector: |
||||
|
target: |
||||
|
entity: |
||||
|
domain: light |
||||
|
no_motion_wait: |
||||
|
name: Wait time |
||||
|
description: Time to leave the light on after last motion is detected. |
||||
|
default: 120 |
||||
|
selector: |
||||
|
number: |
||||
|
min: 0 |
||||
|
max: 3600 |
||||
|
unit_of_measurement: seconds |
||||
|
|
||||
|
# If motion is detected within the delay, |
||||
|
# we restart the script. |
||||
|
mode: restart |
||||
|
max_exceeded: silent |
||||
|
|
||||
|
trigger: |
||||
|
platform: state |
||||
|
entity_id: !input motion_entity |
||||
|
from: "off" |
||||
|
to: "on" |
||||
|
|
||||
|
action: |
||||
|
- service: light.turn_on |
||||
|
target: !input light_target |
||||
|
- wait_for_trigger: |
||||
|
platform: state |
||||
|
entity_id: !input motion_entity |
||||
|
from: "on" |
||||
|
to: "off" |
||||
|
- delay: !input no_motion_wait |
||||
|
- service: light.turn_off |
||||
|
target: !input light_target |
@ -0,0 +1,43 @@ |
|||||
|
blueprint: |
||||
|
name: Zone Notification |
||||
|
description: Send a notification to a device when a person leaves a specific zone. |
||||
|
domain: automation |
||||
|
source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml |
||||
|
input: |
||||
|
person_entity: |
||||
|
name: Person |
||||
|
selector: |
||||
|
entity: |
||||
|
domain: person |
||||
|
zone_entity: |
||||
|
name: Zone |
||||
|
selector: |
||||
|
entity: |
||||
|
domain: zone |
||||
|
notify_device: |
||||
|
name: Device to notify |
||||
|
description: Device needs to run the official Home Assistant app to receive notifications. |
||||
|
selector: |
||||
|
device: |
||||
|
integration: mobile_app |
||||
|
|
||||
|
trigger: |
||||
|
platform: state |
||||
|
entity_id: !input person_entity |
||||
|
|
||||
|
variables: |
||||
|
zone_entity: !input zone_entity |
||||
|
# This is the state of the person when it's in this zone. |
||||
|
zone_state: "{{ states[zone_entity].name }}" |
||||
|
person_entity: !input person_entity |
||||
|
person_name: "{{ states[person_entity].name }}" |
||||
|
|
||||
|
condition: |
||||
|
condition: template |
||||
|
value_template: "{{ trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}" |
||||
|
|
||||
|
action: |
||||
|
domain: mobile_app |
||||
|
type: notify |
||||
|
device_id: !input notify_device |
||||
|
message: "{{ person_name }} has left {{ zone_state }}" |
@ -0,0 +1,64 @@ |
|||||
|
homeassistant: |
||||
|
name: Home Assistant KR |
||||
|
latitude: !secret latitude_home |
||||
|
longitude: !secret longitude_home |
||||
|
elevation: !secret elevation_home |
||||
|
temperature_unit: C |
||||
|
time_zone: Europe/Moscow |
||||
|
unit_system: metric |
||||
|
|
||||
|
zone: |
||||
|
- name: Home |
||||
|
latitude: !secret latitude_home |
||||
|
longitude: !secret longitude_home |
||||
|
radius: 200 |
||||
|
icon: mdi:home |
||||
|
|
||||
|
default_config: |
||||
|
|
||||
|
tts: |
||||
|
- platform: google_translate |
||||
|
|
||||
|
automation: !include automations.yaml |
||||
|
group: !include groups.yaml |
||||
|
recorder: !include recorder.yaml |
||||
|
scene: !include scenes.yaml |
||||
|
script: !include scripts.yaml |
||||
|
|
||||
|
# https://www.home-assistant.io/integrations/yeelight |
||||
|
yeelight: |
||||
|
devices: |
||||
|
10.10.10.10: |
||||
|
name: Room unit0 |
||||
|
transition: 1000 |
||||
|
use_music_mode: false |
||||
|
save_on_change: false |
||||
|
model: color4 |
||||
|
10.10.10.11: |
||||
|
name: Room unit1 |
||||
|
transition: 1000 |
||||
|
use_music_mode: false |
||||
|
save_on_change: false |
||||
|
model: color4 |
||||
|
10.10.10.12: |
||||
|
name: Room unit2 |
||||
|
transition: 1000 |
||||
|
use_music_mode: false |
||||
|
save_on_change: false |
||||
|
model: color4 |
||||
|
10.10.10.13: |
||||
|
name: Room unit3 |
||||
|
transition: 1000 |
||||
|
use_music_mode: false |
||||
|
save_on_change: false |
||||
|
model: color4 |
||||
|
|
||||
|
# https://github.com/claytonjn/hass-circadian_lighting |
||||
|
circadian_lighting: |
||||
|
switch: |
||||
|
- platform: circadian_lighting |
||||
|
lights_ct: |
||||
|
- light.room_unit0 |
||||
|
- light.room_unit1 |
||||
|
- light.room_unit2 |
||||
|
- light.room_unit3 |
@ -0,0 +1,316 @@ |
|||||
|
""" |
||||
|
Circadian Lighting Component for Home-Assistant. |
||||
|
|
||||
|
This component calculates color temperature and brightness to synchronize |
||||
|
your color changing lights with perceived color temperature of the sky throughout |
||||
|
the day. This gives your environment a more natural feel, with cooler whites during |
||||
|
the midday and warmer tints near twilight and dawn. |
||||
|
|
||||
|
In addition, the component sets your lights to a nice warm white at 1% in "Sleep" mode, |
||||
|
which is far brighter than starlight but won't reset your circadian rhythm or break down |
||||
|
too much rhodopsin in your eyes. |
||||
|
|
||||
|
Human circadian rhythms are heavily influenced by ambient light levels and |
||||
|
hues. Hormone production, brainwave activity, mood and wakefulness are |
||||
|
just some of the cognitive functions tied to cyclical natural light. |
||||
|
http://en.wikipedia.org/wiki/Zeitgeber |
||||
|
|
||||
|
Here's some further reading: |
||||
|
|
||||
|
http://www.cambridgeincolour.com/tutorials/sunrise-sunset-calculator.htm |
||||
|
http://en.wikipedia.org/wiki/Color_temperature |
||||
|
|
||||
|
Technical notes: I had to make a lot of assumptions when writing this app |
||||
|
* There are no considerations for weather or altitude, but does use your |
||||
|
hub's location to calculate the sun position. |
||||
|
* The component doesn't calculate a true "Blue Hour" -- it just sets the |
||||
|
lights to 2700K (warm white) until your hub goes into Night mode |
||||
|
""" |
||||
|
|
||||
|
import logging |
||||
|
|
||||
|
import voluptuous as vol |
||||
|
|
||||
|
import homeassistant.helpers.config_validation as cv |
||||
|
from homeassistant.components.light import ( |
||||
|
VALID_TRANSITION, ATTR_TRANSITION) |
||||
|
from homeassistant.const import ( |
||||
|
CONF_LATITUDE, CONF_LONGITUDE, CONF_ELEVATION, |
||||
|
SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET) |
||||
|
from homeassistant.util import Throttle |
||||
|
from homeassistant.helpers.discovery import load_platform |
||||
|
from homeassistant.helpers.dispatcher import dispatcher_send |
||||
|
from homeassistant.helpers.event import track_sunrise, track_sunset, track_time_change |
||||
|
from homeassistant.util.color import ( |
||||
|
color_temperature_to_rgb, color_RGB_to_xy, |
||||
|
color_xy_to_hs) |
||||
|
from homeassistant.util.dt import now as dt_now, get_time_zone |
||||
|
|
||||
|
from datetime import datetime, timedelta |
||||
|
|
||||
|
VERSION = '1.0.13' |
||||
|
|
||||
|
_LOGGER = logging.getLogger(__name__) |
||||
|
|
||||
|
DOMAIN = 'circadian_lighting' |
||||
|
CIRCADIAN_LIGHTING_PLATFORMS = ['sensor', 'switch'] |
||||
|
CIRCADIAN_LIGHTING_UPDATE_TOPIC = '{0}_update'.format(DOMAIN) |
||||
|
DATA_CIRCADIAN_LIGHTING = 'data_cl' |
||||
|
|
||||
|
CONF_MIN_CT = 'min_colortemp' |
||||
|
DEFAULT_MIN_CT = 2500 |
||||
|
CONF_MAX_CT = 'max_colortemp' |
||||
|
DEFAULT_MAX_CT = 5500 |
||||
|
CONF_SUNRISE_OFFSET = 'sunrise_offset' |
||||
|
CONF_SUNSET_OFFSET = 'sunset_offset' |
||||
|
CONF_SUNRISE_TIME = 'sunrise_time' |
||||
|
CONF_SUNSET_TIME = 'sunset_time' |
||||
|
CONF_INTERVAL = 'interval' |
||||
|
DEFAULT_INTERVAL = 300 |
||||
|
DEFAULT_TRANSITION = 60 |
||||
|
|
||||
|
CONFIG_SCHEMA = vol.Schema({ |
||||
|
DOMAIN: vol.Schema({ |
||||
|
vol.Optional(CONF_MIN_CT, default=DEFAULT_MIN_CT): |
||||
|
vol.All(vol.Coerce(int), vol.Range(min=1000, max=10000)), |
||||
|
vol.Optional(CONF_MAX_CT, default=DEFAULT_MAX_CT): |
||||
|
vol.All(vol.Coerce(int), vol.Range(min=1000, max=10000)), |
||||
|
vol.Optional(CONF_SUNRISE_OFFSET): cv.time_period_str, |
||||
|
vol.Optional(CONF_SUNSET_OFFSET): cv.time_period_str, |
||||
|
vol.Optional(CONF_SUNRISE_TIME): cv.time, |
||||
|
vol.Optional(CONF_SUNSET_TIME): cv.time, |
||||
|
vol.Optional(CONF_LATITUDE): cv.latitude, |
||||
|
vol.Optional(CONF_LONGITUDE): cv.longitude, |
||||
|
vol.Optional(CONF_ELEVATION): float, |
||||
|
vol.Optional(CONF_INTERVAL, default=DEFAULT_INTERVAL): cv.positive_int, |
||||
|
vol.Optional(ATTR_TRANSITION, default=DEFAULT_TRANSITION): VALID_TRANSITION |
||||
|
}), |
||||
|
}, extra=vol.ALLOW_EXTRA) |
||||
|
|
||||
|
def setup(hass, config): |
||||
|
"""Set up the Circadian Lighting component.""" |
||||
|
conf = config[DOMAIN] |
||||
|
min_colortemp = conf.get(CONF_MIN_CT) |
||||
|
max_colortemp = conf.get(CONF_MAX_CT) |
||||
|
sunrise_offset = conf.get(CONF_SUNRISE_OFFSET) |
||||
|
sunset_offset = conf.get(CONF_SUNSET_OFFSET) |
||||
|
sunrise_time = conf.get(CONF_SUNRISE_TIME) |
||||
|
sunset_time = conf.get(CONF_SUNSET_TIME) |
||||
|
|
||||
|
latitude = conf.get(CONF_LATITUDE, hass.config.latitude) |
||||
|
longitude = conf.get(CONF_LONGITUDE, hass.config.longitude) |
||||
|
elevation = conf.get(CONF_ELEVATION, hass.config.elevation) |
||||
|
|
||||
|
load_platform(hass, 'sensor', DOMAIN, {}, config) |
||||
|
|
||||
|
interval = conf.get(CONF_INTERVAL) |
||||
|
transition = conf.get(ATTR_TRANSITION) |
||||
|
|
||||
|
cl = CircadianLighting(hass, min_colortemp, max_colortemp, |
||||
|
sunrise_offset, sunset_offset, sunrise_time, sunset_time, |
||||
|
latitude, longitude, elevation, |
||||
|
interval, transition) |
||||
|
|
||||
|
hass.data[DATA_CIRCADIAN_LIGHTING] = cl |
||||
|
|
||||
|
return True |
||||
|
|
||||
|
class CircadianLighting(object): |
||||
|
"""Calculate universal Circadian values.""" |
||||
|
|
||||
|
def __init__(self, hass, min_colortemp, max_colortemp, |
||||
|
sunrise_offset, sunset_offset, sunrise_time, sunset_time, |
||||
|
latitude, longitude, elevation, |
||||
|
interval, transition): |
||||
|
self.hass = hass |
||||
|
self.data = {} |
||||
|
self.data['min_colortemp'] = min_colortemp |
||||
|
self.data['max_colortemp'] = max_colortemp |
||||
|
self.data['sunrise_offset'] = sunrise_offset |
||||
|
self.data['sunset_offset'] = sunset_offset |
||||
|
self.data['sunrise_time'] = sunrise_time |
||||
|
self.data['sunset_time'] = sunset_time |
||||
|
self.data['latitude'] = latitude |
||||
|
self.data['longitude'] = longitude |
||||
|
self.data['elevation'] = elevation |
||||
|
self.data['interval'] = interval |
||||
|
self.data['transition'] = transition |
||||
|
self.data['timezone'] = self.get_timezone() |
||||
|
self.data['percent'] = self.calc_percent() |
||||
|
self.data['colortemp'] = self.calc_colortemp() |
||||
|
self.data['rgb_color'] = self.calc_rgb() |
||||
|
self.data['xy_color'] = self.calc_xy() |
||||
|
self.data['hs_color'] = self.calc_hs() |
||||
|
|
||||
|
self.update = Throttle(timedelta(seconds=interval))(self._update) |
||||
|
|
||||
|
if self.data['sunrise_time'] is not None: |
||||
|
track_time_change(self.hass, self._update, hour=int(self.data['sunrise_time'].strftime("%H")), minute=int(self.data['sunrise_time'].strftime("%M")), second=int(self.data['sunrise_time'].strftime("%S"))) |
||||
|
else: |
||||
|
track_sunrise(self.hass, self._update, self.data['sunrise_offset']) |
||||
|
if self.data['sunset_time'] is not None: |
||||
|
track_time_change(self.hass, self._update, hour=int(self.data['sunset_time'].strftime("%H")), minute=int(self.data['sunset_time'].strftime("%M")), second=int(self.data['sunset_time'].strftime("%S"))) |
||||
|
else: |
||||
|
track_sunset(self.hass, self._update, self.data['sunset_offset']) |
||||
|
|
||||
|
def get_timezone(self): |
||||
|
from timezonefinder import TimezoneFinder |
||||
|
tf = TimezoneFinder() |
||||
|
timezone_string = tf.timezone_at(lng=self.data['longitude'], lat=self.data['latitude']) |
||||
|
timezone = get_time_zone(timezone_string) |
||||
|
_LOGGER.debug("Timezone: " + str(timezone)) |
||||
|
return timezone |
||||
|
|
||||
|
def get_sunrise_sunset(self, date = None): |
||||
|
if self.data['sunrise_time'] is not None and self.data['sunset_time'] is not None: |
||||
|
if date is None: |
||||
|
date = dt_now(self.data['timezone']) |
||||
|
sunrise = date.replace(hour=int(self.data['sunrise_time'].strftime("%H")), minute=int(self.data['sunrise_time'].strftime("%M")), second=int(self.data['sunrise_time'].strftime("%S")), microsecond=int(self.data['sunrise_time'].strftime("%f"))) |
||||
|
sunset = date.replace(hour=int(self.data['sunset_time'].strftime("%H")), minute=int(self.data['sunset_time'].strftime("%M")), second=int(self.data['sunset_time'].strftime("%S")), microsecond=int(self.data['sunset_time'].strftime("%f"))) |
||||
|
solar_noon = sunrise + (sunset - sunrise)/2 |
||||
|
solar_midnight = sunset + ((sunrise + timedelta(days=1)) - sunset)/2 |
||||
|
else: |
||||
|
import astral |
||||
|
location = astral.Location() |
||||
|
location.name = 'name' |
||||
|
location.region = 'region' |
||||
|
location.latitude = self.data['latitude'] |
||||
|
location.longitude = self.data['longitude'] |
||||
|
location.elevation = self.data['elevation'] |
||||
|
_LOGGER.debug("Astral location: " + str(location)) |
||||
|
if self.data['sunrise_time'] is not None: |
||||
|
if date is None: |
||||
|
date = dt_now(self.data['timezone']) |
||||
|
sunrise = date.replace(hour=int(self.data['sunrise_time'].strftime("%H")), minute=int(self.data['sunrise_time'].strftime("%M")), second=int(self.data['sunrise_time'].strftime("%S")), microsecond=int(self.data['sunrise_time'].strftime("%f"))) |
||||
|
else: |
||||
|
sunrise = location.sunrise(date) |
||||
|
if self.data['sunset_time'] is not None: |
||||
|
if date is None: |
||||
|
date = dt_now(self.data['timezone']) |
||||
|
sunset = date.replace(hour=int(self.data['sunset_time'].strftime("%H")), minute=int(self.data['sunset_time'].strftime("%M")), second=int(self.data['sunset_time'].strftime("%S")), microsecond=int(self.data['sunset_time'].strftime("%f"))) |
||||
|
else: |
||||
|
sunset = location.sunset(date) |
||||
|
solar_noon = location.solar_noon(date) |
||||
|
solar_midnight = location.solar_midnight(date) |
||||
|
if self.data['sunrise_offset'] is not None: |
||||
|
sunrise = sunrise + self.data['sunrise_offset'] |
||||
|
if self.data['sunset_offset'] is not None: |
||||
|
sunset = sunset + self.data['sunset_offset'] |
||||
|
return { |
||||
|
SUN_EVENT_SUNRISE: sunrise.astimezone(self.data['timezone']), |
||||
|
SUN_EVENT_SUNSET: sunset.astimezone(self.data['timezone']), |
||||
|
'solar_noon': solar_noon.astimezone(self.data['timezone']), |
||||
|
'solar_midnight': solar_midnight.astimezone(self.data['timezone']) |
||||
|
} |
||||
|
|
||||
|
def calc_percent(self): |
||||
|
now = dt_now(self.data['timezone']) |
||||
|
_LOGGER.debug("now: " + str(now)) |
||||
|
|
||||
|
today_sun_times = self.get_sunrise_sunset(now) |
||||
|
_LOGGER.debug("today_sun_times: " + str(today_sun_times)) |
||||
|
|
||||
|
# Convert everything to epoch timestamps for easy calculation |
||||
|
now_seconds = now.timestamp() |
||||
|
sunrise_seconds = today_sun_times[SUN_EVENT_SUNRISE].timestamp() |
||||
|
sunset_seconds = today_sun_times[SUN_EVENT_SUNSET].timestamp() |
||||
|
solar_noon_seconds = today_sun_times['solar_noon'].timestamp() |
||||
|
solar_midnight_seconds = today_sun_times['solar_midnight'].timestamp() |
||||
|
|
||||
|
if now < today_sun_times[SUN_EVENT_SUNRISE]: # It's before sunrise (after midnight) |
||||
|
# Because it's before sunrise (and after midnight) sunset must have happend yesterday |
||||
|
yesterday_sun_times = self.get_sunrise_sunset(now - timedelta(days=1)) |
||||
|
_LOGGER.debug("yesterday_sun_times: " + str(yesterday_sun_times)) |
||||
|
sunset_seconds = yesterday_sun_times[SUN_EVENT_SUNSET].timestamp() |
||||
|
if today_sun_times['solar_midnight'] > today_sun_times[SUN_EVENT_SUNSET] and yesterday_sun_times['solar_midnight'] > yesterday_sun_times[SUN_EVENT_SUNSET]: |
||||
|
# Solar midnight is after sunset so use yesterdays's time |
||||
|
solar_midnight_seconds = yesterday_sun_times['solar_midnight'].timestamp() |
||||
|
elif now > today_sun_times[SUN_EVENT_SUNSET]: # It's after sunset (before midnight) |
||||
|
# Because it's after sunset (and before midnight) sunrise should happen tomorrow |
||||
|
tomorrow_sun_times = self.get_sunrise_sunset(now + timedelta(days=1)) |
||||
|
_LOGGER.debug("tomorrow_sun_times: " + str(tomorrow_sun_times)) |
||||
|
sunrise_seconds = tomorrow_sun_times[SUN_EVENT_SUNRISE].timestamp() |
||||
|
if today_sun_times['solar_midnight'] < today_sun_times[SUN_EVENT_SUNRISE] and tomorrow_sun_times['solar_midnight'] < tomorrow_sun_times[SUN_EVENT_SUNRISE]: |
||||
|
# Solar midnight is before sunrise so use tomorrow's time |
||||
|
solar_midnight_seconds = tomorrow_sun_times['solar_midnight'].timestamp() |
||||
|
|
||||
|
_LOGGER.debug("now_seconds: " + str(now_seconds)) |
||||
|
_LOGGER.debug("sunrise_seconds: " + str(sunrise_seconds)) |
||||
|
_LOGGER.debug("sunset_seconds: " + str(sunset_seconds)) |
||||
|
_LOGGER.debug("solar_midnight_seconds: " + str(solar_midnight_seconds)) |
||||
|
_LOGGER.debug("solar_noon_seconds: " + str(solar_noon_seconds)) |
||||
|
|
||||
|
# 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 |
||||
|
# because it might not be half way between sunrise and sunset |
||||
|
# We're also (obviously) generating a different parabola for sunrise-sunset |
||||
|
|
||||
|
# sunrise-sunset parabola |
||||
|
if now_seconds > sunrise_seconds and now_seconds < sunset_seconds: |
||||
|
h = solar_noon_seconds |
||||
|
k = 100 |
||||
|
# parabola before solar_noon |
||||
|
if now_seconds < solar_noon_seconds: |
||||
|
x = sunrise_seconds |
||||
|
# parabola after solar_noon |
||||
|
else: |
||||
|
x = sunset_seconds |
||||
|
y = 0 |
||||
|
|
||||
|
# sunset_sunrise parabola |
||||
|
elif now_seconds > sunset_seconds and now_seconds < sunrise_seconds: |
||||
|
h = solar_midnight_seconds |
||||
|
k = -100 |
||||
|
# parabola before solar_midnight |
||||
|
if now_seconds < solar_midnight_seconds: |
||||
|
x = sunset_seconds |
||||
|
# parabola after solar_midnight |
||||
|
else: |
||||
|
x = sunrise_seconds |
||||
|
y = 0 |
||||
|
|
||||
|
a = (y-k)/(h-x)**2 |
||||
|
percentage = a*(now_seconds-h)**2+k |
||||
|
|
||||
|
_LOGGER.debug("h: " + str(h)) |
||||
|
_LOGGER.debug("k: " + str(k)) |
||||
|
_LOGGER.debug("x: " + str(x)) |
||||
|
_LOGGER.debug("y: " + str(y)) |
||||
|
_LOGGER.debug("a: " + str(a)) |
||||
|
_LOGGER.debug("percentage: " + str(percentage)) |
||||
|
|
||||
|
return percentage |
||||
|
|
||||
|
def calc_colortemp(self): |
||||
|
if self.data['percent'] > 0: |
||||
|
return ((self.data['max_colortemp'] - self.data['min_colortemp']) * (self.data['percent'] / 100)) + self.data['min_colortemp'] |
||||
|
else: |
||||
|
return self.data['min_colortemp'] |
||||
|
|
||||
|
def calc_rgb(self): |
||||
|
return color_temperature_to_rgb(self.data['colortemp']) |
||||
|
|
||||
|
def calc_xy(self): |
||||
|
rgb = self.calc_rgb() |
||||
|
iR = rgb[0] |
||||
|
iG = rgb[1] |
||||
|
iB = rgb[2] |
||||
|
|
||||
|
return color_RGB_to_xy(iR, iG, iB) |
||||
|
|
||||
|
def calc_hs(self): |
||||
|
xy = self.calc_xy() |
||||
|
vX = xy[0] |
||||
|
vY = xy[1] |
||||
|
|
||||
|
return color_xy_to_hs(vX, vY) |
||||
|
|
||||
|
def _update(self, *args, **kwargs): |
||||
|
"""Update Circadian Values.""" |
||||
|
self.data['percent'] = self.calc_percent() |
||||
|
self.data['colortemp'] = self.calc_colortemp() |
||||
|
self.data['rgb_color'] = self.calc_rgb() |
||||
|
self.data['xy_color'] = self.calc_xy() |
||||
|
self.data['hs_color'] = self.calc_hs() |
||||
|
dispatcher_send(self.hass, CIRCADIAN_LIGHTING_UPDATE_TOPIC) |
||||
|
_LOGGER.debug("Circadian Lighting Component Updated") |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"domain": "circadian_lighting", |
||||
|
"name": "Circadian Lighting", |
||||
|
"documentation": "https://github.com/claytonjn/hass-circadian_lighting", |
||||
|
"dependencies": [], |
||||
|
"codeowners": ["@claytonjn"], |
||||
|
"requirements": ["timezonefinder==4.2.0"] |
||||
|
} |
@ -0,0 +1,104 @@ |
|||||
|
""" |
||||
|
Circadian Lighting Sensor for Home-Assistant. |
||||
|
""" |
||||
|
|
||||
|
DEPENDENCIES = ['circadian_lighting'] |
||||
|
|
||||
|
import logging |
||||
|
|
||||
|
from custom_components.circadian_lighting import DOMAIN, CIRCADIAN_LIGHTING_UPDATE_TOPIC, DATA_CIRCADIAN_LIGHTING |
||||
|
|
||||
|
from homeassistant.helpers.dispatcher import dispatcher_connect |
||||
|
from homeassistant.helpers.entity import Entity |
||||
|
|
||||
|
import datetime |
||||
|
|
||||
|
_LOGGER = logging.getLogger(__name__) |
||||
|
|
||||
|
ICON = 'mdi:theme-light-dark' |
||||
|
|
||||
|
def setup_platform(hass, config, add_devices, discovery_info=None): |
||||
|
"""Set up the Circadian Lighting sensor.""" |
||||
|
cl = hass.data.get(DATA_CIRCADIAN_LIGHTING) |
||||
|
if cl: |
||||
|
cs = CircadianSensor(hass, cl) |
||||
|
add_devices([cs]) |
||||
|
|
||||
|
def update(call=None): |
||||
|
"""Update component.""" |
||||
|
cl._update() |
||||
|
service_name = "values_update" |
||||
|
hass.services.register(DOMAIN, service_name, update) |
||||
|
return True |
||||
|
else: |
||||
|
return False |
||||
|
|
||||
|
class CircadianSensor(Entity): |
||||
|
"""Representation of a Circadian Lighting sensor.""" |
||||
|
|
||||
|
def __init__(self, hass, cl): |
||||
|
"""Initialize the Circadian Lighting sensor.""" |
||||
|
self._cl = cl |
||||
|
self._name = 'Circadian Values' |
||||
|
self._entity_id = 'sensor.circadian_values' |
||||
|
self._state = self._cl.data['percent'] |
||||
|
self._unit_of_measurement = '%' |
||||
|
self._icon = ICON |
||||
|
self._hs_color = self._cl.data['hs_color'] |
||||
|
self._attributes = {} |
||||
|
self._attributes['colortemp'] = self._cl.data['colortemp'] |
||||
|
self._attributes['rgb_color'] = self._cl.data['rgb_color'] |
||||
|
self._attributes['xy_color'] = self._cl.data['xy_color'] |
||||
|
|
||||
|
"""Register callbacks.""" |
||||
|
dispatcher_connect(hass, CIRCADIAN_LIGHTING_UPDATE_TOPIC, self.update_sensor) |
||||
|
|
||||
|
@property |
||||
|
def entity_id(self): |
||||
|
"""Return the entity ID of the sensor.""" |
||||
|
return self._entity_id |
||||
|
|
||||
|
@property |
||||
|
def name(self): |
||||
|
"""Return the name of the sensor.""" |
||||
|
return self._name |
||||
|
|
||||
|
@property |
||||
|
def state(self): |
||||
|
"""Return the state of the sensor.""" |
||||
|
return self._state |
||||
|
|
||||
|
@property |
||||
|
def unit_of_measurement(self): |
||||
|
"""Return the unit of measurement.""" |
||||
|
return self._unit_of_measurement |
||||
|
|
||||
|
@property |
||||
|
def icon(self): |
||||
|
"""Icon to use in the frontend, if any.""" |
||||
|
return self._icon |
||||
|
|
||||
|
@property |
||||
|
def hs_color(self): |
||||
|
return self._hs_color |
||||
|
|
||||
|
@property |
||||
|
def device_state_attributes(self): |
||||
|
"""Return the attributes of the sensor.""" |
||||
|
return self._attributes |
||||
|
|
||||
|
def update(self): |
||||
|
"""Fetch new state data for the sensor. |
||||
|
|
||||
|
This is the only method that should fetch new data for Home Assistant. |
||||
|
""" |
||||
|
self._cl.update() |
||||
|
|
||||
|
def update_sensor(self): |
||||
|
if self._cl.data is not None: |
||||
|
self._state = self._cl.data['percent'] |
||||
|
self._hs_color = self._cl.data['hs_color'] |
||||
|
self._attributes['colortemp'] = self._cl.data['colortemp'] |
||||
|
self._attributes['rgb_color'] = self._cl.data['rgb_color'] |
||||
|
self._attributes['xy_color'] = self._cl.data['xy_color'] |
||||
|
_LOGGER.debug("Circadian Lighting Sensor Updated") |
@ -0,0 +1,2 @@ |
|||||
|
values_update: |
||||
|
description: Updates values for Circadian Lighting. |
@ -0,0 +1,362 @@ |
|||||
|
""" |
||||
|
Circadian Lighting Switch for Home-Assistant. |
||||
|
""" |
||||
|
|
||||
|
DEPENDENCIES = ['circadian_lighting', 'light'] |
||||
|
|
||||
|
import logging |
||||
|
|
||||
|
from custom_components.circadian_lighting import DOMAIN, CIRCADIAN_LIGHTING_UPDATE_TOPIC, DATA_CIRCADIAN_LIGHTING |
||||
|
|
||||
|
import voluptuous as vol |
||||
|
|
||||
|
import homeassistant.helpers.config_validation as cv |
||||
|
from homeassistant.helpers.dispatcher import dispatcher_connect |
||||
|
from homeassistant.helpers.event import track_state_change |
||||
|
from homeassistant.helpers.restore_state import RestoreEntity |
||||
|
from homeassistant.components.light import ( |
||||
|
is_on, ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_RGB_COLOR, ATTR_TRANSITION, |
||||
|
VALID_TRANSITION, ATTR_WHITE_VALUE, ATTR_XY_COLOR, DOMAIN as LIGHT_DOMAIN) |
||||
|
|
||||
|
try: |
||||
|
from homeassistant.components.switch import SwitchEntity |
||||
|
except ImportError: |
||||
|
from homeassistant.components.switch import SwitchDevice as SwitchEntity |
||||
|
|
||||
|
from homeassistant.const import ( |
||||
|
ATTR_ENTITY_ID, CONF_NAME, CONF_PLATFORM, STATE_ON, |
||||
|
SERVICE_TURN_ON) |
||||
|
from homeassistant.util import slugify |
||||
|
from homeassistant.util.color import ( |
||||
|
color_RGB_to_xy, color_temperature_kelvin_to_mired, |
||||
|
color_temperature_to_rgb, color_xy_to_hs) |
||||
|
|
||||
|
_LOGGER = logging.getLogger(__name__) |
||||
|
|
||||
|
ICON = 'mdi:theme-light-dark' |
||||
|
|
||||
|
CONF_LIGHTS_CT = 'lights_ct' |
||||
|
CONF_LIGHTS_RGB = 'lights_rgb' |
||||
|
CONF_LIGHTS_XY = 'lights_xy' |
||||
|
CONF_LIGHTS_BRIGHT = 'lights_brightness' |
||||
|
CONF_DISABLE_BRIGHTNESS_ADJUST = 'disable_brightness_adjust' |
||||
|
CONF_MIN_BRIGHT = 'min_brightness' |
||||
|
DEFAULT_MIN_BRIGHT = 1 |
||||
|
CONF_MAX_BRIGHT = 'max_brightness' |
||||
|
DEFAULT_MAX_BRIGHT = 100 |
||||
|
CONF_SLEEP_ENTITY = 'sleep_entity' |
||||
|
CONF_SLEEP_STATE = 'sleep_state' |
||||
|
CONF_SLEEP_CT = 'sleep_colortemp' |
||||
|
CONF_SLEEP_BRIGHT = 'sleep_brightness' |
||||
|
CONF_DISABLE_ENTITY = 'disable_entity' |
||||
|
CONF_DISABLE_STATE = 'disable_state' |
||||
|
CONF_INITIAL_TRANSITION = 'initial_transition' |
||||
|
DEFAULT_INITIAL_TRANSITION = 1 |
||||
|
|
||||
|
PLATFORM_SCHEMA = vol.Schema({ |
||||
|
vol.Required(CONF_PLATFORM): 'circadian_lighting', |
||||
|
vol.Optional(CONF_NAME, default="Circadian Lighting"): cv.string, |
||||
|
vol.Optional(CONF_LIGHTS_CT): cv.entity_ids, |
||||
|
vol.Optional(CONF_LIGHTS_RGB): cv.entity_ids, |
||||
|
vol.Optional(CONF_LIGHTS_XY): cv.entity_ids, |
||||
|
vol.Optional(CONF_LIGHTS_BRIGHT): cv.entity_ids, |
||||
|
vol.Optional(CONF_DISABLE_BRIGHTNESS_ADJUST, default=False): cv.boolean, |
||||
|
vol.Optional(CONF_MIN_BRIGHT, default=DEFAULT_MIN_BRIGHT): |
||||
|
vol.All(vol.Coerce(int), vol.Range(min=1, max=100)), |
||||
|
vol.Optional(CONF_MAX_BRIGHT, default=DEFAULT_MAX_BRIGHT): |
||||
|
vol.All(vol.Coerce(int), vol.Range(min=1, max=100)), |
||||
|
vol.Optional(CONF_SLEEP_ENTITY): cv.entity_id, |
||||
|
vol.Optional(CONF_SLEEP_STATE): cv.string, |
||||
|
vol.Optional(CONF_SLEEP_CT): |
||||
|
vol.All(vol.Coerce(int), vol.Range(min=1000, max=10000)), |
||||
|
vol.Optional(CONF_SLEEP_BRIGHT): |
||||
|
vol.All(vol.Coerce(int), vol.Range(min=1, max=100)), |
||||
|
vol.Optional(CONF_DISABLE_ENTITY): cv.entity_id, |
||||
|
vol.Optional(CONF_DISABLE_STATE): cv.string, |
||||
|
vol.Optional(CONF_INITIAL_TRANSITION, default=DEFAULT_INITIAL_TRANSITION): |
||||
|
VALID_TRANSITION |
||||
|
}) |
||||
|
|
||||
|
def setup_platform(hass, config, add_devices, discovery_info=None): |
||||
|
"""Set up the Circadian Lighting switches.""" |
||||
|
cl = hass.data.get(DATA_CIRCADIAN_LIGHTING) |
||||
|
if cl: |
||||
|
lights_ct = config.get(CONF_LIGHTS_CT) |
||||
|
lights_rgb = config.get(CONF_LIGHTS_RGB) |
||||
|
lights_xy = config.get(CONF_LIGHTS_XY) |
||||
|
lights_brightness = config.get(CONF_LIGHTS_BRIGHT) |
||||
|
disable_brightness_adjust = config.get(CONF_DISABLE_BRIGHTNESS_ADJUST) |
||||
|
name = config.get(CONF_NAME) |
||||
|
min_brightness = config.get(CONF_MIN_BRIGHT) |
||||
|
max_brightness = config.get(CONF_MAX_BRIGHT) |
||||
|
sleep_entity = config.get(CONF_SLEEP_ENTITY) |
||||
|
sleep_state = config.get(CONF_SLEEP_STATE) |
||||
|
sleep_colortemp = config.get(CONF_SLEEP_CT) |
||||
|
sleep_brightness = config.get(CONF_SLEEP_BRIGHT) |
||||
|
disable_entity = config.get(CONF_DISABLE_ENTITY) |
||||
|
disable_state = config.get(CONF_DISABLE_STATE) |
||||
|
initial_transition = config.get(CONF_INITIAL_TRANSITION) |
||||
|
cs = CircadianSwitch(hass, cl, name, lights_ct, lights_rgb, lights_xy, lights_brightness, |
||||
|
disable_brightness_adjust, min_brightness, max_brightness, |
||||
|
sleep_entity, sleep_state, sleep_colortemp, sleep_brightness, |
||||
|
disable_entity, disable_state, initial_transition) |
||||
|
add_devices([cs]) |
||||
|
|
||||
|
def update(call=None): |
||||
|
"""Update lights.""" |
||||
|
cs.update_switch() |
||||
|
return True |
||||
|
else: |
||||
|
return False |
||||
|
|
||||
|
|
||||
|
class CircadianSwitch(SwitchEntity, RestoreEntity): |
||||
|
"""Representation of a Circadian Lighting switch.""" |
||||
|
|
||||
|
def __init__(self, hass, cl, name, lights_ct, lights_rgb, lights_xy, lights_brightness, |
||||
|
disable_brightness_adjust, min_brightness, max_brightness, |
||||
|
sleep_entity, sleep_state, sleep_colortemp, sleep_brightness, |
||||
|
disable_entity, disable_state, initial_transition): |
||||
|
"""Initialize the Circadian Lighting switch.""" |
||||
|
self.hass = hass |
||||
|
self._cl = cl |
||||
|
self._name = name |
||||
|
self._entity_id = "switch." + slugify("{} {}".format('circadian_lighting', name)) |
||||
|
self._state = None |
||||
|
self._icon = ICON |
||||
|
self._hs_color = None |
||||
|
self._lights_ct = lights_ct |
||||
|
self._lights_rgb = lights_rgb |
||||
|
self._lights_xy = lights_xy |
||||
|
self._lights_brightness = lights_brightness |
||||
|
self._disable_brightness_adjust = disable_brightness_adjust |
||||
|
self._min_brightness = min_brightness |
||||
|
self._max_brightness = max_brightness |
||||
|
self._sleep_entity = sleep_entity |
||||
|
self._sleep_state = sleep_state |
||||
|
self._sleep_colortemp = sleep_colortemp |
||||
|
self._sleep_brightness = sleep_brightness |
||||
|
self._disable_entity = disable_entity |
||||
|
self._disable_state = disable_state |
||||
|
self._initial_transition = initial_transition |
||||
|
self._attributes = {} |
||||
|
self._attributes['hs_color'] = self._hs_color |
||||
|
self._attributes['brightness'] = None |
||||
|
|
||||
|
self._lights = [] |
||||
|
if lights_ct != None: |
||||
|
self._lights += lights_ct |
||||
|
if lights_rgb != None: |
||||
|
self._lights += lights_rgb |
||||
|
if lights_xy != None: |
||||
|
self._lights += lights_xy |
||||
|
if lights_brightness != None: |
||||
|
self._lights += lights_brightness |
||||
|
|
||||
|
"""Register callbacks.""" |
||||
|
dispatcher_connect(hass, CIRCADIAN_LIGHTING_UPDATE_TOPIC, self.update_switch) |
||||
|
track_state_change(hass, self._lights, self.light_state_changed) |
||||
|
if self._sleep_entity is not None: |
||||
|
track_state_change(hass, self._sleep_entity, self.sleep_state_changed) |
||||
|
if self._disable_entity is not None: |
||||
|
track_state_change(hass, self._disable_entity, self.disable_state_changed) |
||||
|
|
||||
|
@property |
||||
|
def entity_id(self): |
||||
|
"""Return the entity ID of the switch.""" |
||||
|
return self._entity_id |
||||
|
|
||||
|
@property |
||||
|
def name(self): |
||||
|
"""Return the name of the device if any.""" |
||||
|
return self._name |
||||
|
|
||||
|
@property |
||||
|
def is_on(self): |
||||
|
"""Return true if circadian lighting is on.""" |
||||
|
return self._state |
||||
|
|
||||
|
async def async_added_to_hass(self): |
||||
|
"""Call when entity about to be added to hass.""" |
||||
|
# If not None, we got an initial value. |
||||
|
await super().async_added_to_hass() |
||||
|
if self._state is not None: |
||||
|
return |
||||
|
|
||||
|
state = await self.async_get_last_state() |
||||
|
self._state = state and state.state == STATE_ON |
||||
|
|
||||
|
@property |
||||
|
def icon(self): |
||||
|
"""Icon to use in the frontend, if any.""" |
||||
|
return self._icon |
||||
|
|
||||
|
@property |
||||
|
def hs_color(self): |
||||
|
return self._hs_color |
||||
|
|
||||
|
@property |
||||
|
def device_state_attributes(self): |
||||
|
"""Return the attributes of the switch.""" |
||||
|
return self._attributes |
||||
|
|
||||
|
def turn_on(self, **kwargs): |
||||
|
"""Turn on circadian lighting.""" |
||||
|
self._state = True |
||||
|
|
||||
|
# Make initial update |
||||
|
self.update_switch(self._initial_transition) |
||||
|
|
||||
|
self.schedule_update_ha_state() |
||||
|
|
||||
|
def turn_off(self, **kwargs): |
||||
|
"""Turn off circadian lighting.""" |
||||
|
self._state = False |
||||
|
self.schedule_update_ha_state() |
||||
|
self._hs_color = None |
||||
|
self._attributes['hs_color'] = self._hs_color |
||||
|
self._attributes['brightness'] = None |
||||
|
|
||||
|
def is_sleep(self): |
||||
|
return self._sleep_entity is not None and self.hass.states.get(self._sleep_entity).state == self._sleep_state |
||||
|
|
||||
|
def calc_ct(self): |
||||
|
if self.is_sleep(): |
||||
|
_LOGGER.debug(self._name + " in Sleep mode") |
||||
|
return color_temperature_kelvin_to_mired(self._sleep_colortemp) |
||||
|
else: |
||||
|
return color_temperature_kelvin_to_mired(self._cl.data['colortemp']) |
||||
|
|
||||
|
def calc_rgb(self): |
||||
|
if self.is_sleep(): |
||||
|
_LOGGER.debug(self._name + " in Sleep mode") |
||||
|
return color_temperature_to_rgb(self._sleep_colortemp) |
||||
|
else: |
||||
|
return color_temperature_to_rgb(self._cl.data['colortemp']) |
||||
|
|
||||
|
def calc_xy(self): |
||||
|
return color_RGB_to_xy(*self.calc_rgb()) |
||||
|
|
||||
|
def calc_hs(self): |
||||
|
return color_xy_to_hs(*self.calc_xy()) |
||||
|
|
||||
|
def calc_brightness(self): |
||||
|
if self._disable_brightness_adjust is True: |
||||
|
return None |
||||
|
else: |
||||
|
if self.is_sleep(): |
||||
|
_LOGGER.debug(self._name + " in Sleep mode") |
||||
|
return self._sleep_brightness |
||||
|
else: |
||||
|
if self._cl.data['percent'] > 0: |
||||
|
return self._max_brightness |
||||
|
else: |
||||
|
return ((self._max_brightness - self._min_brightness) * ((100+self._cl.data['percent']) / 100)) + self._min_brightness |
||||
|
|
||||
|
def update_switch(self, transition=None): |
||||
|
if self._cl.data is not None: |
||||
|
self._hs_color = self.calc_hs() |
||||
|
self._attributes['hs_color'] = self._hs_color |
||||
|
self._attributes['brightness'] = self.calc_brightness() |
||||
|
_LOGGER.debug(self._name + " Switch Updated") |
||||
|
|
||||
|
self.adjust_lights(self._lights, transition) |
||||
|
|
||||
|
def should_adjust(self): |
||||
|
if self._state is not True: |
||||
|
_LOGGER.debug(self._name + " off - not adjusting") |
||||
|
return False |
||||
|
elif self._cl.data is None: |
||||
|
_LOGGER.debug(self._name + " could not retrieve Circadian Lighting data") |
||||
|
return False |
||||
|
elif self._disable_entity is not None and self.hass.states.get(self._disable_entity).state == self._disable_state: |
||||
|
_LOGGER.debug(self._name + " disabled by " + str(self._disable_entity)) |
||||
|
return False |
||||
|
else: |
||||
|
return True |
||||
|
|
||||
|
def adjust_lights(self, lights, transition=None): |
||||
|
if self.should_adjust(): |
||||
|
if transition == None: |
||||
|
transition = self._cl.data['transition'] |
||||
|
|
||||
|
brightness = int((self._attributes['brightness'] / 100) * 254) if self._attributes['brightness'] is not None else None |
||||
|
mired = int(self.calc_ct()) if self._lights_ct is not None else None |
||||
|
rgb = tuple(map(int, self.calc_rgb())) if self._lights_rgb is not None else None |
||||
|
xy = self.calc_xy() if self._lights_xy is not None else None |
||||
|
|
||||
|
for light in lights: |
||||
|
"""Set color of array of ct light if on.""" |
||||
|
if self._lights_ct is not None and light in self._lights_ct and is_on(self.hass, light): |
||||
|
service_data = {ATTR_ENTITY_ID: light} |
||||
|
if mired is not None: |
||||
|
service_data[ATTR_COLOR_TEMP] = mired |
||||
|
if brightness is not None: |
||||
|
service_data[ATTR_BRIGHTNESS] = brightness |
||||
|
if transition is not None: |
||||
|
service_data[ATTR_TRANSITION] = transition |
||||
|
self.hass.services.call( |
||||
|
LIGHT_DOMAIN, SERVICE_TURN_ON, service_data) |
||||
|
_LOGGER.debug(light + " CT Adjusted - color_temp: " + str(mired) + ", brightness: " + str(brightness) + ", transition: " + str(transition)) |
||||
|
|
||||
|
"""Set color of array of rgb light if on.""" |
||||
|
if self._lights_rgb is not None and light in self._lights_rgb and is_on(self.hass, light): |
||||
|
service_data = {ATTR_ENTITY_ID: light} |
||||
|
if rgb is not None: |
||||
|
service_data[ATTR_RGB_COLOR] = rgb |
||||
|
if brightness is not None: |
||||
|
service_data[ATTR_BRIGHTNESS] = brightness |
||||
|
if transition is not None: |
||||
|
service_data[ATTR_TRANSITION] = transition |
||||
|
self.hass.services.call( |
||||
|
LIGHT_DOMAIN, SERVICE_TURN_ON, service_data) |
||||
|
_LOGGER.debug(light + " RGB Adjusted - rgb_color: " + str(rgb) + ", brightness: " + str(brightness) + ", transition: " + str(transition)) |
||||
|
|
||||
|
"""Set color of array of xy light if on.""" |
||||
|
if self._lights_xy is not None and light in self._lights_xy and is_on(self.hass, light): |
||||
|
service_data = {ATTR_ENTITY_ID: light} |
||||
|
if xy is not None: |
||||
|
service_data[ATTR_XY_COLOR] = xy |
||||
|
if brightness is not None: |
||||
|
service_data[ATTR_BRIGHTNESS] = brightness |
||||
|
service_data[ATTR_WHITE_VALUE] = brightness |
||||
|
if transition is not None: |
||||
|
service_data[ATTR_TRANSITION] = transition |
||||
|
self.hass.services.call( |
||||
|
LIGHT_DOMAIN, SERVICE_TURN_ON, service_data) |
||||
|
_LOGGER.debug(light + " XY Adjusted - xy_color: " + str(xy) + ", brightness: " + str(brightness) + ", transition: " + str(transition) + ", white_value: " + str(brightness)) |
||||
|
|
||||
|
"""Set color of array of brightness light if on.""" |
||||
|
if self._lights_brightness is not None and light in self._lights_brightness and is_on(self.hass, light): |
||||
|
service_data = {ATTR_ENTITY_ID: light} |
||||
|
if brightness is not None: |
||||
|
service_data[ATTR_BRIGHTNESS] = brightness |
||||
|
if transition is not None: |
||||
|
service_data[ATTR_TRANSITION] = transition |
||||
|
self.hass.services.call( |
||||
|
LIGHT_DOMAIN, SERVICE_TURN_ON, service_data) |
||||
|
_LOGGER.debug(light + " Brightness Adjusted - brightness: " + str(brightness) + ", transition: " + str(transition)) |
||||
|
|
||||
|
def light_state_changed(self, entity_id, from_state, to_state): |
||||
|
try: |
||||
|
_LOGGER.debug(entity_id + " change from " + str(from_state) + " to " + str(to_state)) |
||||
|
if to_state.state == 'on' and from_state.state != 'on': |
||||
|
self.adjust_lights([entity_id], self._initial_transition) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
def sleep_state_changed(self, entity_id, from_state, to_state): |
||||
|
try: |
||||
|
_LOGGER.debug(entity_id + " change from " + str(from_state) + " to " + str(to_state)) |
||||
|
if to_state.state == self._sleep_state or from_state.state == self._sleep_state: |
||||
|
self.update_switch(self._initial_transition) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
def disable_state_changed(self, entity_id, from_state, to_state): |
||||
|
try: |
||||
|
_LOGGER.debug(entity_id + " change from " + str(from_state) + " to " + str(to_state)) |
||||
|
if from_state.state == self._disable_state: |
||||
|
self.update_switch(self._initial_transition) |
||||
|
except: |
||||
|
pass |
@ -0,0 +1 @@ |
|||||
|
db_url: !secret recorder_db_url |
Reference in new issue