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

View File

@ -1,21 +1,15 @@
""" """Tests for the device tracker compoment."""
tests.components.device_tracker.test_init
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests the device tracker compoments.
"""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from datetime import datetime, timedelta from datetime import datetime, timedelta
import os import os
from homeassistant.config import load_yaml_config_file
from homeassistant.loader import get_component from homeassistant.loader import get_component
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME, ATTR_HIDDEN, 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 import homeassistant.components.device_tracker as device_tracker
from tests.common import ( 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.const import CONF_PLATFORM, STATE_HOME, STATE_NOT_HOME
from homeassistant.components import ( from homeassistant.components import (
device_tracker, light, sun, device_sun_light_trigger) device_tracker, light, sun, device_sun_light_trigger)
from homeassistant.helpers import event_decorators
from tests.common import ( from tests.common import (
get_test_config_dir, get_test_home_assistant, ensure_sun_risen, 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 def setUp(self): # pylint: disable=invalid-name
self.hass = get_test_home_assistant() self.hass = get_test_home_assistant()
event_decorators.HASS = self.hass
self.scanner = loader.get_component( self.scanner = loader.get_component(
'device_tracker.test').get_scanner(None, None) 'device_tracker.test').get_scanner(None, None)
@ -64,6 +65,7 @@ class TestDeviceSunLightTrigger(unittest.TestCase):
def tearDown(self): # pylint: disable=invalid-name def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """ """ Stop down stuff we started. """
self.hass.stop() self.hass.stop()
event_decorators.HASS = None
def test_lights_on_when_sun_sets(self): def test_lights_on_when_sun_sets(self):
""" Test lights go on when there is someone home and the sun sets. """ """ 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 def tearDown(self): # pylint: disable=invalid-name
""" Stop down stuff we started. """ """ Stop down stuff we started. """
self.hass.stop() self.hass.stop()
event_decorators.HASS = None
def test_track_sunrise(self): def test_track_sunrise(self):
""" Test track sunrise decorator """ """ Test track sunrise decorator """