Device Sun Light Trigger: Clean up

This commit is contained in:
Paulus Schoutsen 2016-03-05 19:56:50 -08:00
parent bdad69307a
commit cd30f2de0d
4 changed files with 72 additions and 97 deletions

View File

@ -1,8 +1,6 @@
"""
homeassistant.components.device_sun_light_trigger
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides functionality to turn on lights based on the state of the sun and
devices.
devices home.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/device_sun_light_trigger/
@ -12,9 +10,9 @@ from datetime import timedelta
import homeassistant.util.dt as dt_util
from homeassistant.const import STATE_HOME, STATE_NOT_HOME
from homeassistant.helpers.event import track_point_in_time, track_state_change
from . import device_tracker, group, light, sun
from homeassistant.helpers.event import track_point_in_time
from homeassistant.helpers.event_decorators import track_state_change
from homeassistant.loader import get_component
DOMAIN = "device_sun_light_trigger"
DEPENDENCIES = ['light', 'device_tracker', 'group', 'sun']
@ -29,28 +27,26 @@ CONF_LIGHT_GROUP = 'light_group'
CONF_DEVICE_GROUP = 'device_group'
# pylint: disable=too-many-branches
# pylint: disable=too-many-locals
def setup(hass, config):
""" Triggers to turn lights on or off based on device precense. """
logger = logging.getLogger(__name__)
device_tracker = get_component('device_tracker')
group = get_component('group')
light = get_component('light')
sun = get_component('sun')
disable_turn_off = 'disable_turn_off' in config[DOMAIN]
light_group = config[DOMAIN].get(CONF_LIGHT_GROUP,
light.ENTITY_ID_ALL_LIGHTS)
light_profile = config[DOMAIN].get(CONF_LIGHT_PROFILE, LIGHT_PROFILE)
device_group = config[DOMAIN].get(CONF_DEVICE_GROUP,
device_tracker.ENTITY_ID_ALL_DEVICES)
logger = logging.getLogger(__name__)
device_entity_ids = group.get_entity_ids(hass, device_group,
device_tracker.DOMAIN)
if not device_entity_ids:
logger.error("No devices found to track")
return False
# Get the light IDs from the specified group
@ -58,77 +54,68 @@ def setup(hass, config):
if not light_ids:
logger.error("No lights found to turn on ")
return False
def calc_time_for_light_when_sunset():
""" Calculates the time when to start fading lights in when sun sets.
Returns None if no next_setting data available. """
next_setting = sun.next_setting(hass)
if next_setting:
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
else:
if not next_setting:
return None
def schedule_light_on_sun_rise(entity, old_state, new_state):
"""The moment sun sets we want to have all the lights on.
We will schedule to have each light start after one another
and slowly transition in."""
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
def turn_light_on_before_sunset(light_id):
""" Helper function to turn on lights slowly if there
are devices home and the light is not on yet. """
if device_tracker.is_on(hass) and not light.is_on(hass, light_id):
if not device_tracker.is_on(hass) or light.is_on(hass, light_id):
return
light.turn_on(hass, light_id,
transition=LIGHT_TRANSITION_TIME.seconds,
profile=light_profile)
# Track every time sun rises so we can schedule a time-based
# pre-sun set event
@track_state_change(sun.ENTITY_ID, sun.STATE_BELOW_HORIZON,
sun.STATE_ABOVE_HORIZON)
def schedule_lights_at_sun_set(hass, entity, old_state, new_state):
"""The moment sun sets we want to have all the lights on.
We will schedule to have each light start after one another
and slowly transition in."""
start_point = calc_time_for_light_when_sunset()
if not start_point:
return
def turn_on(light_id):
""" Lambda can keep track of function parameters but not local
parameters. If we put the lambda directly in the below statement
only the last light will be turned on.. """
return lambda now: turn_light_on_before_sunset(light_id)
start_point = calc_time_for_light_when_sunset()
if start_point:
for index, light_id in enumerate(light_ids):
track_point_in_time(
hass, turn_on(light_id),
(start_point + index * LIGHT_TRANSITION_TIME))
# Track every time sun rises so we can schedule a time-based
# pre-sun set event
track_state_change(hass, sun.ENTITY_ID, schedule_light_on_sun_rise,
sun.STATE_BELOW_HORIZON, sun.STATE_ABOVE_HORIZON)
track_point_in_time(hass, turn_on(light_id),
start_point + index * LIGHT_TRANSITION_TIME)
# If the sun is already above horizon
# schedule the time-based pre-sun set event
if sun.is_on(hass):
schedule_light_on_sun_rise(None, None, None)
schedule_lights_at_sun_set(hass, None, None, None)
def check_light_on_dev_state_change(entity, old_state, new_state):
""" Function to handle tracked device state changes. """
@track_state_change(device_entity_ids, STATE_NOT_HOME, STATE_HOME)
def check_light_on_dev_state_change(hass, entity, old_state, new_state):
"""Handle tracked device state changes."""
# pylint: disable=unused-variable
lights_are_on = group.is_on(hass, light_group)
light_needed = not (lights_are_on or sun.is_on(hass))
# Specific device came home ?
if entity != device_tracker.ENTITY_ID_ALL_DEVICES and \
new_state.state == STATE_HOME:
# These variables are needed for the elif check
now = dt_util.now()
start_point = calc_time_for_light_when_sunset()
# Do we need lights?
if light_needed:
logger.info(
"Home coming event for %s. Turning lights on", entity)
logger.info("Home coming event for %s. Turning lights on", entity)
light.turn_on(hass, light_ids, profile=light_profile)
# Are we in the time span were we would turn on the lights
@ -141,7 +128,6 @@ def setup(hass, config):
# Check for every light if it would be on if someone was home
# when the fading in started and turn it on if so
for index, light_id in enumerate(light_ids):
if now > start_point + index * LIGHT_TRANSITION_TIME:
light.turn_on(hass, light_id)
@ -150,24 +136,16 @@ def setup(hass, config):
# will all the following then, break.
break
# Did all devices leave the house?
elif (entity == device_group and
new_state.state == STATE_NOT_HOME and lights_are_on and
not disable_turn_off):
if not disable_turn_off:
@track_state_change(device_group, STATE_HOME, STATE_NOT_HOME)
def turn_off_lights_when_all_leave(hass, entity, old_state, new_state):
"""Handle device group state change."""
# pylint: disable=unused-variable
if not group.is_on(hass, light_group):
return
logger.info(
"Everyone has left but there are lights on. Turning them off")
light.turn_off(hass, light_ids)
# Track home coming of each device
track_state_change(
hass, device_entity_ids, check_light_on_dev_state_change,
STATE_NOT_HOME, STATE_HOME)
# Track when all devices are gone to shut down lights
track_state_change(
hass, device_group, check_light_on_dev_state_change,
STATE_HOME, STATE_NOT_HOME)
return True

View File

@ -1,21 +1,15 @@
"""
tests.components.device_tracker.test_init
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests the device tracker compoments.
"""
"""Tests for the device tracker compoment."""
# pylint: disable=protected-access,too-many-public-methods
import unittest
from unittest.mock import patch
from datetime import datetime, timedelta
import os
from homeassistant.config import load_yaml_config_file
from homeassistant.loader import get_component
import homeassistant.util.dt as dt_util
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME, ATTR_HIDDEN,
STATE_HOME, STATE_NOT_HOME, CONF_PLATFORM, DEVICE_DEFAULT_NAME)
STATE_HOME, STATE_NOT_HOME, CONF_PLATFORM)
import homeassistant.components.device_tracker as device_tracker
from tests.common import (

View File

@ -7,7 +7,7 @@ import homeassistant.loader as loader
from homeassistant.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME
from homeassistant.components import (
device_tracker, light, sun, device_sun_light_trigger)
from homeassistant.helpers import event_decorators
from tests.common import (
get_test_config_dir, get_test_home_assistant, ensure_sun_risen,
@ -41,6 +41,7 @@ class TestDeviceSunLightTrigger(unittest.TestCase):
def setUp(self): # pylint: disable=invalid-name
self.hass = get_test_home_assistant()
event_decorators.HASS = self.hass
self.scanner = loader.get_component(
'device_tracker.test').get_scanner(None, None)
@ -64,6 +65,7 @@ class TestDeviceSunLightTrigger(unittest.TestCase):
def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """
self.hass.stop()
event_decorators.HASS = None
def test_lights_on_when_sun_sets(self):
""" Test lights go on when there is someone home and the sun sets. """

View File

@ -38,6 +38,7 @@ class TestEventDecoratorHelpers(unittest.TestCase):
def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """
self.hass.stop()
event_decorators.HASS = None
def test_track_sunrise(self):
""" Test track sunrise decorator """