Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
165 commits
Select commit Hold shift + click to select a range
2d805c7
Add adaptive_lighting component
basnijholt Sep 26, 2020
1fe3c81
add a basic implemention to prevent lights from turning on when actua…
basnijholt Sep 26, 2020
ae9ea31
add 'disable_color_adjust' option after request on Reddit
basnijholt Sep 26, 2020
37c33fd
cancel adjusting lights for at least the turn_off transition time
basnijholt Sep 27, 2020
f54db5a
fix pylint issues
basnijholt Sep 27, 2020
6805c04
improve logging
basnijholt Sep 27, 2020
9b5a6d1
light might take longer than specified turn_off 'transition', so wait…
basnijholt Sep 27, 2020
d8ef264
always ignore any event during a turn_off transition
basnijholt Sep 27, 2020
ba21692
ignore all files for coverage
basnijholt Sep 27, 2020
e0b37d0
reduce indentation level in _maybe_cancel
basnijholt Sep 27, 2020
b13aee9
listen to light.turn_on events
basnijholt Sep 27, 2020
30d3066
lock lights while waiting to turn off
basnijholt Sep 28, 2020
513b24d
partially address review comments
basnijholt Sep 28, 2020
8dd7862
add TurnOnOffListener that is used among switches and wait until EVEN…
basnijholt Sep 28, 2020
010580a
simplify maybe_cancel_adjusting to not overflow a light with calls
basnijholt Sep 28, 2020
e1cca6c
catch all turn_off events
basnijholt Sep 28, 2020
5a7bf70
update with latest yaml settings
basnijholt Sep 28, 2020
90867f2
always use color_temp over rgb
basnijholt Sep 28, 2020
9fa6c70
fix tz issue when manually setting sunset/sunrise
basnijholt Sep 28, 2020
0836819
add prefer_rgb_color
basnijholt Sep 28, 2020
77eef65
cancel sleep task when turn_on is called
basnijholt Sep 28, 2020
5c62069
fix issue where lights weren't updated when toggling the switch
basnijholt Sep 28, 2020
378a774
do not return but call self._abort_if_unique_id_configured
basnijholt Sep 28, 2020
d8453ac
rename variables (sorry pylint)
basnijholt Sep 28, 2020
766a1b4
styling
basnijholt Sep 28, 2020
c02faf9
sub/unsub trackers when loading/unloading and turning on/off
basnijholt Sep 28, 2020
ad61f35
fix turning on and off, delay setup listeners until HA start
basnijholt Sep 29, 2020
e02fb0e
define and use switch._unsub_trackers
basnijholt Sep 29, 2020
4cdf404
do not use old settings when reloading config
basnijholt Sep 29, 2020
5feb96e
simplify call to hass.config_entries.async_forward_entry_unload
basnijholt Sep 29, 2020
1403b66
remove assert because it's possible that trackers haven't been set up…
basnijholt Sep 29, 2020
80236f9
actually use the lock
basnijholt Sep 29, 2020
ea615e0
fix documentation url
basnijholt Sep 29, 2020
9a7e102
udpate manifest.json
basnijholt Oct 13, 2020
227d3c1
rename to adjust_brightness, adjust_color_temp, adjust_rgb_color
basnijholt Sep 29, 2020
43d6b07
rename to adapt_brightness, adapt_color_temp, and adapt_rgb_color
basnijholt Sep 29, 2020
704ad0a
rename on_lights_only -> turn_on_lights
basnijholt Sep 29, 2020
02697f5
use adapt_brightness, adapt_color_temp, adapt_rgb_color in adaptive_l…
basnijholt Sep 29, 2020
1203746
use _expand_light_groups in apply service
basnijholt Sep 29, 2020
d655dfe
remove turn on off listener
basnijholt Sep 29, 2020
07e0ed7
rename trackers -> listeners
basnijholt Sep 29, 2020
afc5c64
setup_listeners only when turning on
basnijholt Sep 29, 2020
20dcced
improve strings.json
basnijholt Sep 29, 2020
357661b
do not set state before calling async_turn_on
basnijholt Sep 30, 2020
415c3bb
add strings for adapt_color_temp and adapt_rgb_color
basnijholt Sep 30, 2020
86f4ad1
add comments
basnijholt Sep 30, 2020
66d6ce4
add strings for disable_entity/state, sleep_entity/state
basnijholt Sep 30, 2020
21ff5b1
shorten initial_transition string
basnijholt Sep 30, 2020
3a4a051
turn on switch upon adding to Home Assistant
basnijholt Sep 30, 2020
3d0a208
reword sleep_entity's string
basnijholt Sep 30, 2020
5735434
pass context, add type annotation, and fix several small bugs
basnijholt Sep 30, 2020
2f5fb45
mark _update_attrs as callback
basnijholt Sep 30, 2020
1e717cc
raise an exception instead of assert
basnijholt Oct 1, 2020
82452e7
reorder and rename methods and keep Event objects in TurnOnOffListener
basnijholt Oct 1, 2020
72335be
start implementing 'take_over_control' feature
basnijholt Oct 1, 2020
6f66846
break out in _update_attrs_and_maybe_adapt_lights
basnijholt Oct 2, 2020
d61cc14
make supported_features into a function
basnijholt Oct 2, 2020
0ed8320
extract light settings into SunLightSettings dataclass
basnijholt Oct 2, 2020
b0bed5c
add a sleep_mode switch and remove config options
basnijholt Oct 2, 2020
fa82753
add note in strings.json
basnijholt Oct 2, 2020
52aad67
deprecate disable_entity and disable_state
basnijholt Oct 2, 2020
30b6c0c
style
basnijholt Oct 2, 2020
1b6432a
add 'unique_id'
basnijholt Oct 2, 2020
3ca719b
uncomment unique_id because of exception:
basnijholt Oct 2, 2020
759f585
implement "take_over_control" feature
basnijholt Oct 3, 2020
b294a8e
add async_will_remove_from_hass
basnijholt Oct 3, 2020
bf4e9e1
remove entity_id and use unique_id
basnijholt Oct 3, 2020
ebe60e4
set default manually_controlled[light]=False
basnijholt Oct 3, 2020
79d9a40
use cv.ensure_list
basnijholt Oct 3, 2020
3a933cd
use __getitem__
basnijholt Oct 3, 2020
428395c
simplify conditional
basnijholt Oct 3, 2020
f5222e8
detect significant changes for lights that are changed outside of HA
basnijholt Oct 4, 2020
1be93f1
add "detect_non_ha_changes" option
basnijholt Oct 4, 2020
d63872e
change order of options
basnijholt Oct 4, 2020
0f87320
fix pylint issue
basnijholt Oct 4, 2020
1599a28
avoid ZeroDivisionError
basnijholt Oct 4, 2020
df05877
update strings.json
basnijholt Oct 4, 2020
1599a34
call self.turn_on_off_listener.reset directly
basnijholt Oct 4, 2020
6e0b288
pass context to homeassistant.update_entity
basnijholt Oct 4, 2020
f6cd487
fix case where turn_off_event is None
basnijholt Oct 4, 2020
fadd945
print values in significant_change debug log
basnijholt Oct 4, 2020
854a6d1
only show lights that can be controlled usefully
basnijholt Oct 4, 2020
042c964
fix: if turn_off_event is None it has no data attr
basnijholt Oct 5, 2020
a7d51a6
register service after async_add_entities
basnijholt Oct 5, 2020
be8b17e
change order in OptionsFlow
basnijholt Oct 5, 2020
6249bd5
add 'manually_controlled' attribute to the switch
basnijholt Oct 5, 2020
249f811
implement suggestions by iMicknl and KTibow and add more type-annotat…
basnijholt Oct 5, 2020
21491ed
use 'state_changed' events to check if states changed
basnijholt Oct 5, 2020
6b77d61
reduce COLOR_TEMP_CHANGE to 20 (range is in mired not Kelvin)
basnijholt Oct 6, 2020
b27f80d
remove unused function abs_rel_diff
basnijholt Oct 6, 2020
43d681f
listen to EVENT_HOMEASSISTANT_STARTED instead of EVENT_HOMEASSISTANT_…
basnijholt Oct 6, 2020
3815859
pass unique identifiable contexts to solve bug
basnijholt Oct 6, 2020
d50abc4
make the contexts more identifiable
basnijholt Oct 6, 2020
0e22b63
only log context.id
basnijholt Oct 6, 2020
4e1c584
log the context.id
basnijholt Oct 7, 2020
ce4af77
improve readability
basnijholt Oct 7, 2020
1b981e1
keep context_id shorter than 36 chars
basnijholt Oct 7, 2020
d53442a
add an extra check
basnijholt Oct 7, 2020
5cf5d41
Simplify handling state changes with the same context ID
basnijholt Oct 7, 2020
5dc3286
Return early in the function
basnijholt Oct 7, 2020
4f0c96d
fix: pass Context instead of only the ID
basnijholt Oct 8, 2020
5bcba1f
fix: pass Context instead of only the ID
basnijholt Oct 8, 2020
be0cfe0
only log context.id
basnijholt Oct 8, 2020
a1da04f
log all state change events even the ones that aren't saved
basnijholt Oct 9, 2020
3999d40
compare to latest target state
basnijholt Oct 9, 2020
54097a3
simplify if-elif
basnijholt Oct 9, 2020
8c909d4
change order of options
basnijholt Oct 9, 2020
5b70f7d
Use a different color metric to measure the distance between the colors
basnijholt Oct 11, 2020
4de800c
fix strings.json
basnijholt Oct 11, 2020
57bc163
do not directly mark a light as manually controlled
basnijholt Oct 13, 2020
4f19cd6
fire 'adaptive_lighting_manually_controlled' event
basnijholt Oct 13, 2020
cb1754c
add service 'adaptive_lighting.not_manually_controlled'
basnijholt Oct 13, 2020
e7fb5b0
change 'not_manually_controlled' to 'set_manually_controlled' service
basnijholt Oct 14, 2020
6cfe818
rename event_type to 'adaptive_lighting.manual_control'
basnijholt Oct 14, 2020
312c641
rename 'manually_controlled' to 'manual_control' everywhere (because …
basnijholt Oct 14, 2020
29cd165
fix bug where last_state_change wasn't updated if prev state was not …
basnijholt Oct 16, 2020
59eb820
fix hassfest error
basnijholt Oct 16, 2020
851b117
add tests/components/adaptive_lighting/test_config_flow.py
basnijholt Oct 16, 2020
caa568f
simplify tests/components/adaptive_lighting/test_config_flow.py
basnijholt Oct 16, 2020
71a56fb
add tests/components/adaptive_lighting/test_init.py
basnijholt Oct 17, 2020
e9b972c
add tests/components/adaptive_lighting/test_switch.py
basnijholt Oct 17, 2020
c6b6393
pop after unload
basnijholt Oct 17, 2020
d7b04c7
test for different timezones and fix the problem with sunevents order
basnijholt Oct 17, 2020
287db89
remove adaptive_lighting from .coveragerc
basnijholt Oct 17, 2020
f4948b1
simplify tests
basnijholt Oct 17, 2020
6d8347b
test around sunrise too
basnijholt Oct 17, 2020
039b020
use helper function setup_switch
basnijholt Oct 17, 2020
872bd1c
add tests/components/adaptive_lighting/__init__.py
basnijholt Oct 17, 2020
a35dce9
Remove initial doc-string
basnijholt Oct 17, 2020
045ef33
test light settings and manual control
basnijholt Oct 18, 2020
31122a7
test that toggling switches resets manual control
basnijholt Oct 18, 2020
0c0eff3
use legacy_patchable_time to fix failing tests
basnijholt Oct 18, 2020
33524be
fix pylint issues
basnijholt Oct 18, 2020
e35e0b6
restore dt_util.DEFAULT_TIME_ZONE
basnijholt Oct 18, 2020
9fe4d26
use a fixture to restore the timezone
basnijholt Oct 18, 2020
778d24f
increase coverage to >90%
basnijholt Oct 18, 2020
da30a8b
add more tests
basnijholt Oct 19, 2020
f053860
increase config_flow.py test coverage to 100%
basnijholt Oct 19, 2020
3ebf0e4
increase overal test coverage to >97%
basnijholt Oct 19, 2020
fa54aeb
use FakeLight implementation based on demo.light
basnijholt Oct 19, 2020
7290e19
add SLEEP_MODE_SWITCH constant and clean up tests
basnijholt Oct 19, 2020
1399760
add 'sun_position' attribute
basnijholt Oct 19, 2020
2203995
use DemoLight for as long as it works
basnijholt Oct 20, 2020
4732f4e
generalize AdaptiveSleepModeSwitch to SimpleSwitch
basnijholt Oct 20, 2020
1b9451b
define a adapt_brightness and adapt_color switch
basnijholt Oct 21, 2020
bb18134
watch for color or brightness related data in light.turn_on
basnijholt Oct 21, 2020
e74eac2
no need to track the state of adapt_brightness and color switches
basnijholt Oct 21, 2020
ab796e7
add German translations and update strings
basnijholt Oct 21, 2020
d728ec0
fix initial state of SimpleSwitch
basnijholt Oct 22, 2020
ee6ba25
make sure that 'last_state is not None'
basnijholt Oct 22, 2020
19b7651
add test-case to prevent the bug solved in prev commit
basnijholt Oct 22, 2020
d8d3c28
fix and test adaptive_lighting.apply
basnijholt Oct 25, 2020
2a362ea
add Swedish translation
basnijholt Oct 25, 2020
92bd891
support white_value and sync back https://github.com/basnijholt/adapt…
basnijholt Oct 27, 2020
f984c5e
use ATTR_SUPPORTED_FEATURES
basnijholt Oct 27, 2020
eebe687
if lights is not specified in set_manual_control, select all lights
basnijholt Oct 27, 2020
14036c0
add separate_turn_on_commands option for MQTT lights
basnijholt Oct 27, 2020
011cc3e
improve separate_turn_on_commands option
basnijholt Oct 30, 2020
eff9bcc
only adapt lights if switch is on and set_manual_control is called
basnijholt Oct 30, 2020
d0ed089
add switch entity_id to adaptive_lighting.manual_control event
basnijholt Dec 19, 2020
f1bee58
log fire adaptive_lighting.manual_control event
basnijholt Dec 19, 2020
3874641
wait between turn_on commands for transition/2 if separate_turn_on_co…
basnijholt Dec 19, 2020
42d5d53
deal with 'light.turn_on' with multiple lights, which appear as csv list
basnijholt Dec 19, 2020
afe8ce6
fix bug when resetting switch and it's off
basnijholt Dec 27, 2020
5bf889a
separate_turn_on_commands fix when _split_service_data returns 1 item
basnijholt Dec 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ homeassistant/scripts/check_config.py @kellerza
homeassistant/components/abode/* @shred86
homeassistant/components/accuweather/* @bieniu
homeassistant/components/acmeda/* @atmurray
homeassistant/components/adaptive_lighting/* @basnijholt
homeassistant/components/adguard/* @frenck
homeassistant/components/advantage_air/* @Bre77
homeassistant/components/agent_dvr/* @ispysoftware
Expand Down
90 changes: 90 additions & 0 deletions homeassistant/components/adaptive_lighting/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""Adaptive Lighting integration in Home-Assistant."""
import logging
from typing import Any, Dict

import voluptuous as vol

from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_SOURCE
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv

Comment thread
basnijholt marked this conversation as resolved.
from .const import (
_DOMAIN_SCHEMA,
ATTR_TURN_ON_OFF_LISTENER,
CONF_NAME,
DOMAIN,
UNDO_UPDATE_LISTENER,
)

_LOGGER = logging.getLogger(__name__)

PLATFORMS = ["switch"]


def _all_unique_names(value):
"""Validate that all entities have a unique profile name."""
hosts = [device[CONF_NAME] for device in value]
schema = vol.Schema(vol.Unique())
schema(hosts)
return value


CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.All(cv.ensure_list, [_DOMAIN_SCHEMA], _all_unique_names)},
extra=vol.ALLOW_EXTRA,
)


async def async_setup(hass: HomeAssistant, config: Dict[str, Any]):
"""Import integration from config."""

if DOMAIN in config:
for entry in config[DOMAIN]:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_IMPORT}, data=entry
)
)
return True


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
"""Set up the component."""
data = hass.data.setdefault(DOMAIN, {})

undo_listener = config_entry.add_update_listener(async_update_options)
data[config_entry.entry_id] = {UNDO_UPDATE_LISTENER: undo_listener}
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)

return True


async def async_update_options(hass, config_entry: ConfigEntry):
"""Update options."""
await hass.config_entries.async_reload(config_entry.entry_id)


async def async_unload_entry(hass, config_entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_forward_entry_unload(
config_entry, "switch"
)
data = hass.data[DOMAIN]
data[config_entry.entry_id][UNDO_UPDATE_LISTENER]()
if unload_ok:
data.pop(config_entry.entry_id)

if len(data) == 1 and ATTR_TURN_ON_OFF_LISTENER in data:
# no more config_entries
turn_on_off_listener = data.pop(ATTR_TURN_ON_OFF_LISTENER)
turn_on_off_listener.remove_listener()
turn_on_off_listener.remove_listener2()

if not data:
hass.data.pop(DOMAIN)

return unload_ok
109 changes: 109 additions & 0 deletions homeassistant/components/adaptive_lighting/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""Config flow for Adaptive Lighting integration."""
import logging

import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv

from .const import ( # pylint: disable=unused-import
CONF_LIGHTS,
DOMAIN,
EXTRA_VALIDATION,
NONE_STR,
VALIDATION_TUPLES,
)
from .switch import _supported_features

_LOGGER = logging.getLogger(__name__)


class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Adaptive Lighting."""

VERSION = 1

async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}

if user_input is not None:
await self.async_set_unique_id(user_input[CONF_NAME])
self._abort_if_unique_id_configured()
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)

return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_NAME): str}),
errors=errors,
)

async def async_step_import(self, user_input=None):
"""Handle configuration by yaml file."""
await self.async_set_unique_id(user_input[CONF_NAME])
for entry in self._async_current_entries():
if entry.unique_id == self.unique_id:
self.hass.config_entries.async_update_entry(entry, data=user_input)
self._abort_if_unique_id_configured()
return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)

@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return OptionsFlowHandler(config_entry)


def validate_options(user_input, errors):
"""Validate the options in the OptionsFlow.

This is an extra validation step because the validators
in `EXTRA_VALIDATION` cannot be serialized to json.
"""
for key, (validate, _) in EXTRA_VALIDATION.items():
# these are unserializable validators
value = user_input.get(key)
try:
if value is not None and value != NONE_STR:
validate(value)
except vol.Invalid:
_LOGGER.exception("Configuration option %s=%s is incorrect", key, value)
errors["base"] = "option_error"


class OptionsFlowHandler(config_entries.OptionsFlow):
"""Handle a option flow for Adaptive Lighting."""

def __init__(self, config_entry: config_entries.ConfigEntry):
"""Initialize options flow."""
self.config_entry = config_entry

async def async_step_init(self, user_input=None):
"""Handle options flow."""
conf = self.config_entry
if conf.source == config_entries.SOURCE_IMPORT:
return self.async_show_form(step_id="init", data_schema=None)
errors = {}
if user_input is not None:
validate_options(user_input, errors)
if not errors:
return self.async_create_entry(title="", data=user_input)

all_lights = [
light
for light in self.hass.states.async_entity_ids("light")
if _supported_features(self.hass, light)
]
to_replace = {CONF_LIGHTS: cv.multi_select(sorted(all_lights))}

options_schema = {}
for name, default, validation in VALIDATION_TUPLES:
key = vol.Optional(name, default=conf.options.get(name, default))
value = to_replace.get(name, validation)
options_schema[key] = value

return self.async_show_form(
step_id="init", data_schema=vol.Schema(options_schema), errors=errors
)
127 changes: 127 additions & 0 deletions homeassistant/components/adaptive_lighting/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
"""Constants for the Adaptive Lighting integration."""
import voluptuous as vol

from homeassistant.components.light import VALID_TRANSITION
import homeassistant.helpers.config_validation as cv

ICON = "mdi:theme-light-dark"

DOMAIN = "adaptive_lighting"
SUN_EVENT_NOON = "solar_noon"
SUN_EVENT_MIDNIGHT = "solar_midnight"

CONF_NAME, DEFAULT_NAME = "name", "default"
CONF_LIGHTS, DEFAULT_LIGHTS = "lights", []
CONF_DETECT_NON_HA_CHANGES, DEFAULT_DETECT_NON_HA_CHANGES = (
"detect_non_ha_changes",
False,
)
CONF_INITIAL_TRANSITION, DEFAULT_INITIAL_TRANSITION = "initial_transition", 1
CONF_INTERVAL, DEFAULT_INTERVAL = "interval", 90
CONF_MAX_BRIGHTNESS, DEFAULT_MAX_BRIGHTNESS = "max_brightness", 100
CONF_MAX_COLOR_TEMP, DEFAULT_MAX_COLOR_TEMP = "max_color_temp", 5500
CONF_MIN_BRIGHTNESS, DEFAULT_MIN_BRIGHTNESS = "min_brightness", 1
CONF_MIN_COLOR_TEMP, DEFAULT_MIN_COLOR_TEMP = "min_color_temp", 2000
CONF_ONLY_ONCE, DEFAULT_ONLY_ONCE = "only_once", False
CONF_PREFER_RGB_COLOR, DEFAULT_PREFER_RGB_COLOR = "prefer_rgb_color", False
CONF_SEPARATE_TURN_ON_COMMANDS, DEFAULT_SEPARATE_TURN_ON_COMMANDS = (
"separate_turn_on_commands",
False,
)
CONF_SLEEP_BRIGHTNESS, DEFAULT_SLEEP_BRIGHTNESS = "sleep_brightness", 1
CONF_SLEEP_COLOR_TEMP, DEFAULT_SLEEP_COLOR_TEMP = "sleep_color_temp", 1000
CONF_SUNRISE_OFFSET, DEFAULT_SUNRISE_OFFSET = "sunrise_offset", 0
CONF_SUNRISE_TIME = "sunrise_time"
CONF_SUNSET_OFFSET, DEFAULT_SUNSET_OFFSET = "sunset_offset", 0
CONF_SUNSET_TIME = "sunset_time"
CONF_TAKE_OVER_CONTROL, DEFAULT_TAKE_OVER_CONTROL = "take_over_control", True
CONF_TRANSITION, DEFAULT_TRANSITION = "transition", 45

SLEEP_MODE_SWITCH = "sleep_mode_switch"
ADAPT_COLOR_SWITCH = "adapt_color_switch"
ADAPT_BRIGHTNESS_SWITCH = "adapt_brightness_switch"
ATTR_TURN_ON_OFF_LISTENER = "turn_on_off_listener"
UNDO_UPDATE_LISTENER = "undo_update_listener"
NONE_STR = "None"
ATTR_ADAPT_COLOR = "adapt_color"
ATTR_ADAPT_BRIGHTNESS = "adapt_brightness"

SERVICE_SET_MANUAL_CONTROL = "set_manual_control"
CONF_MANUAL_CONTROL = "manual_control"
SERVICE_APPLY = "apply"
CONF_TURN_ON_LIGHTS = "turn_on_lights"

TURNING_OFF_DELAY = 5


def int_between(min_int, max_int):
"""Return an integer between 'min_int' and 'max_int'."""
return vol.All(vol.Coerce(int), vol.Range(min=min_int, max=max_int))


VALIDATION_TUPLES = [
(CONF_LIGHTS, DEFAULT_LIGHTS, cv.entity_ids),
(CONF_PREFER_RGB_COLOR, DEFAULT_PREFER_RGB_COLOR, bool),
(CONF_INITIAL_TRANSITION, DEFAULT_INITIAL_TRANSITION, VALID_TRANSITION),
(CONF_TRANSITION, DEFAULT_TRANSITION, VALID_TRANSITION),
(CONF_INTERVAL, DEFAULT_INTERVAL, cv.positive_int),
(CONF_MIN_BRIGHTNESS, DEFAULT_MIN_BRIGHTNESS, int_between(1, 100)),
(CONF_MAX_BRIGHTNESS, DEFAULT_MAX_BRIGHTNESS, int_between(1, 100)),
(CONF_MIN_COLOR_TEMP, DEFAULT_MIN_COLOR_TEMP, int_between(1000, 10000)),
(CONF_MAX_COLOR_TEMP, DEFAULT_MAX_COLOR_TEMP, int_between(1000, 10000)),
(CONF_SLEEP_BRIGHTNESS, DEFAULT_SLEEP_BRIGHTNESS, int_between(1, 100)),
(CONF_SLEEP_COLOR_TEMP, DEFAULT_SLEEP_COLOR_TEMP, int_between(1000, 10000)),
(CONF_SUNRISE_TIME, NONE_STR, str),
(CONF_SUNRISE_OFFSET, DEFAULT_SUNRISE_OFFSET, int),
(CONF_SUNSET_TIME, NONE_STR, str),
(CONF_SUNSET_OFFSET, DEFAULT_SUNSET_OFFSET, int),
(CONF_ONLY_ONCE, DEFAULT_ONLY_ONCE, bool),
(CONF_TAKE_OVER_CONTROL, DEFAULT_TAKE_OVER_CONTROL, bool),
(CONF_DETECT_NON_HA_CHANGES, DEFAULT_DETECT_NON_HA_CHANGES, bool),
(CONF_SEPARATE_TURN_ON_COMMANDS, DEFAULT_SEPARATE_TURN_ON_COMMANDS, bool),
]


def timedelta_as_int(value):
"""Convert a `datetime.timedelta` object to an integer.

This integer can be serialized to json but a timedelta cannot.
"""
return value.total_seconds()


# conf_option: (validator, coerce) tuples
# these validators cannot be serialized but can be serialized when coerced by coerce.
EXTRA_VALIDATION = {
CONF_INTERVAL: (cv.time_period, timedelta_as_int),
CONF_SUNRISE_OFFSET: (cv.time_period, timedelta_as_int),
CONF_SUNRISE_TIME: (cv.time, str),
CONF_SUNSET_OFFSET: (cv.time_period, timedelta_as_int),
CONF_SUNSET_TIME: (cv.time, str),
}


def maybe_coerce(key, validation):
"""Coerce the validation into a json serializable type."""
validation, coerce = EXTRA_VALIDATION.get(key, (validation, None))
if coerce is not None:
return vol.All(validation, vol.Coerce(coerce))
return validation


def replace_none_str(value, replace_with=None):
"""Replace "None" -> replace_with."""
return value if value != NONE_STR else replace_with


_yaml_validation_tuples = [
(key, default, maybe_coerce(key, validation))
for key, default, validation in VALIDATION_TUPLES
] + [(CONF_NAME, DEFAULT_NAME, cv.string)]

_DOMAIN_SCHEMA = vol.Schema(
{
vol.Optional(key, default=replace_none_str(default, vol.UNDEFINED)): validation
for key, default, validation in _yaml_validation_tuples
}
)
9 changes: 9 additions & 0 deletions homeassistant/components/adaptive_lighting/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"domain": "adaptive_lighting",
"name": "Adaptive Lighting",
"documentation": "https://www.home-assistant.io/integrations/adaptive_lighting",
"config_flow": true,
"dependencies": [],
"codeowners": ["@basnijholt"],
"requirements": []
}
36 changes: 36 additions & 0 deletions homeassistant/components/adaptive_lighting/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
apply:
description: Applies the current Adaptive Lighting settings to lights.
fields:
entity_id:
description: entity_id of the Adaptive Lighting switch.
example: switch.adaptive_lighting_default
lights:
description: entity_id(s) of lights.
example: light.bedroom_ceiling
transition:
description: Transition of the lights.
example: 10
adapt_brightness:
description: "Adapt the 'brightness', default: true"
example: true
adapt_color:
description: "Adapt the color_temp/color_rgb, default: true"
example: true
prefer_rgb_color:
description: "Prefer to use color_rgb over color_temp if possible, default: false"
example: false
turn_on_lights:
description: "Turn on the lights that are off, default: false"
example: false
set_manual_control:
description: Mark whether a light is 'manually controlled'.
fields:
entity_id:
description: entity_id of the Adaptive Lighting switch.
example: switch.adaptive_lighting_default
manual_control:
description: "Whether to add ('true') or remove ('false') the light from the 'manual_control' list, default: true"
example: true
lights:
description: entity_id(s) of lights, if not specified, all lights in the switch are selected.
example: light.bedroom_ceiling
Loading