Code cleanup and new sun set transition

This commit is contained in:
Paulus Schoutsen 2013-09-20 19:29:15 -07:00
parent 9732bf99ac
commit 5f4ddfe92c
4 changed files with 50 additions and 38 deletions

View File

@ -9,8 +9,7 @@ STATE_DEVICE_DEFAULT = STATE_DEVICE_NOT_HOME
# After how much time do we consider a device not home if
# it does not show up on scans
# 70 seconds is to ensure 2 scans
TIME_SPAN_FOR_ERROR_IN_SCANNING = timedelta(seconds=70)
TIME_SPAN_FOR_ERROR_IN_SCANNING = timedelta(seconds=60)
STATE_CATEGORY_ALL_DEVICES = 'device.alldevices'
STATE_CATEGORY_DEVICE_FORMAT = 'device.{}'
@ -27,9 +26,9 @@ class DeviceTracker(object):
temp_devices_to_track = device_scanner.get_devices_to_track()
self.devices_to_track = { device: { 'name': temp_devices_to_track[device],
'last_seen': default_last_seen,
'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) }
for device in temp_devices_to_track }
'last_seen': default_last_seen,
'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) }
for device in temp_devices_to_track }
# Add categories to state machine
statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT)
@ -41,8 +40,7 @@ class DeviceTracker(object):
def device_state_categories(self):
for device in self.devices_to_track:
yield self.devices_to_track[device]['category']
return [self.devices_to_track[device]['category'] for device in self.devices_to_track]
def set_state(self, device, state):
@ -53,7 +51,8 @@ class DeviceTracker(object):
def update_devices(self, found_devices):
# Keep track of devices that are home, all that are not will be marked not home
"""Keep track of devices that are home, all that are not will be marked not home"""
temp_tracking_devices = self.devices_to_track.keys()
for device in found_devices:
@ -71,7 +70,7 @@ class DeviceTracker(object):
if datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
self.set_state(device, STATE_DEVICE_NOT_HOME)
# Get the set of currently used statuses
# Get the currently used statuses
states_of_devices = [self.statemachine.get_state(self.devices_to_track[device]['category']).state for device in self.devices_to_track]
all_devices_state = STATE_DEVICE_HOME if STATE_DEVICE_HOME in states_of_devices else STATE_DEVICE_NOT_HOME

View File

@ -33,14 +33,18 @@ class StateMachine(object):
self.lock.release()
def is_state(self, category, state):
assert category in self.states, "Category does not exist: {}".format(category)
return self.get_state(category).state == state
def get_state(self, category):
assert category in self.states, "Category does not exist: {}".format(category)
return self.states[category]
def get_states(self):
for category in sorted(self.states.keys()):
yield category, self.states[category].state, self.states[category].last_changed
return [(category, self.states[category].state, self.states[category].last_changed) for category in sorted(self.states.keys())]
def track_state_change(eventbus, category, from_state, to_state, action):

View File

@ -1,5 +1,5 @@
import logging
from datetime import datetime, timedelta
from datetime import timedelta
from phue import Bridge
@ -10,6 +10,9 @@ from app.observer.Timer import track_time_change
LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=15)
LIGHT_TRANSITION_TIME_HUE = 9000 # 1/10th seconds
LIGHT_TRANSITION_TIME = timedelta(seconds=LIGHT_TRANSITION_TIME_HUE/10)
class HueTrigger(object):
def __init__(self, config, eventbus, statemachine, device_tracker, weather):
self.eventbus = eventbus
@ -31,57 +34,63 @@ class HueTrigger(object):
track_state_change(eventbus, STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, self.handle_sun_rising)
# If the sun is already above horizon schedule the time-based pre-sun set event
if statemachine.get_state(STATE_CATEGORY_SUN).state == SUN_STATE_ABOVE_HORIZON:
if statemachine.is_state(STATE_CATEGORY_SUN, SUN_STATE_ABOVE_HORIZON):
self.handle_sun_rising(None, None, None)
def get_lights_status(self):
lights_are_on = sum([1 for light in self.lights if light.on]) > 0
light_needed = not lights_are_on and self.statemachine.get_state(STATE_CATEGORY_SUN).state == SUN_STATE_BELOW_HORIZON
light_needed = not lights_are_on and self.statemachine.is_state(STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON)
return lights_are_on, light_needed
def turn_light_on(self, light_id=None, transitiontime=None):
if light_id is None:
light_id = [light.light_id for light in self.lights]
def turn_lights_on(self, transitiontime=None):
command = {'on': True, 'xy': [0.5119, 0.4147], 'bri':164}
if transitiontime is not None:
command['transitiontime'] = transitiontime
self.bridge.set_light([1, 2, 3], command)
self.bridge.set_light(light_id, command)
def turn_lights_off(self, transitiontime=None):
def turn_light_off(self, light_id=None, transitiontime=None):
if light_id is None:
light_id = [light.light_id for light in self.lights]
command = {'on': False}
if transitiontime is not None:
command['transitiontime'] = transitiontime
self.bridge.set_light([1, 2, 3], command)
self.bridge.set_light(light_id, command)
def handle_sun_rising(self, category, old_state, new_state):
# Schedule an event X minutes prior to sun setting
point_in_time = self.weather.next_sun_setting()-LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD
"""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."""
self.logger.info("Will put lights on at {} to compensate less light from setting sun.".format(point_in_time))
start_point = self.weather.next_sun_setting() - LIGHT_TRANSITION_TIME * len(self.lights)
track_time_change(self.eventbus, self.handle_sun_setting, point_in_time=point_in_time)
# Lambda can keep track of function parameters, not from local parameters
# If we put the lambda directly in the below statement only the last light
# would be turned on..
def turn_on(light_id):
return lambda now: self.turn_light_on_before_sunset(light_id)
for index, light in enumerate(self.lights):
track_time_change(self.eventbus, turn_on(light.light_id),
point_in_time=start_point + index * LIGHT_TRANSITION_TIME)
# Gets called when darkness starts falling in, slowly turn on the lights
def handle_sun_setting(self, now):
lights_are_on, light_needed = self.get_lights_status()
if not lights_are_on and self.statemachine.get_state(STATE_CATEGORY_ALL_DEVICES).state == STATE_DEVICE_HOME:
self.logger.info("Sun setting and devices home. Turning on lights.")
# We will start the lights now and by the time the sun sets
# the lights will be at full brightness
transitiontime = (self.weather.next_sun_setting() - datetime.now()).seconds * 10
self.turn_lights_on(transitiontime)
def turn_light_on_before_sunset(self, light_id=None):
"""Helper function to turn on lights slowly if there are devices home and the light is not on yet."""
if self.statemachine.is_state(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_HOME) and not self.bridge.get_light(light_id, 'on'):
self.turn_light_on(light_id, LIGHT_TRANSITION_TIME_HUE)
def handle_device_state_change(self, category, old_state, new_state):
@ -90,9 +99,9 @@ class HueTrigger(object):
# Specific device came home ?
if category != STATE_CATEGORY_ALL_DEVICES and new_state.state == STATE_DEVICE_HOME and light_needed:
self.logger.info("Home coming event for {}. Turning lights on".format(category))
self.turn_lights_on()
self.turn_light_on()
# Did all devices leave the house?
elif category == STATE_CATEGORY_ALL_DEVICES and new_state.state == STATE_DEVICE_NOT_HOME and lights_are_on:
self.logger.info("Everyone has left. Turning lights off")
self.turn_lights_off()
self.turn_light_off()

View File

@ -39,7 +39,7 @@ class Timer(threading.Thread):
break
def track_time_change(eventBus, action, year='*', month='*', day='*', hour='*', minute='*', second='*', point_in_time=None, listen_once=False):
def track_time_change(eventbus, action, year='*', month='*', day='*', hour='*', minute='*', second='*', point_in_time=None, listen_once=False):
year, month, day = ensure_list(year), ensure_list(month), ensure_list(day)
hour, minute, second = ensure_list(hour), ensure_list(minute), ensure_list(second)
@ -60,4 +60,4 @@ def track_time_change(eventBus, action, year='*', month='*', day='*', hour='*',
action(event.data['now'])
eventBus.listen(EVENT_TIME_CHANGED, listener)
eventbus.listen(EVENT_TIME_CHANGED, listener)