Next sun rising and sun setting are now in statemachine.

This commit is contained in:
Paulus Schoutsen 2013-10-06 11:47:24 -07:00
parent cb4fce7691
commit 97e82b3808
3 changed files with 32 additions and 60 deletions

View File

@ -9,13 +9,14 @@ This module provides actors that will react to events happening within homeassis
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
import dateutil.parser
from phue import Bridge from phue import Bridge
from . import track_state_change from . import track_state_change
from .observers import (STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, from .observers import (STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON,
STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME, STATE_CATEGORY_ALL_DEVICES, DEVICE_STATE_HOME, DEVICE_STATE_NOT_HOME,
track_time_change) STATE_CATEGORY_NEXT_SUN_SETTING, track_time_change)
LIGHT_TRANSITION_TIME = timedelta(minutes=15) LIGHT_TRANSITION_TIME = timedelta(minutes=15)
@ -30,10 +31,9 @@ def _hue_process_transition_time(transition_seconds):
class LightTrigger(object): class LightTrigger(object):
""" Class to turn on lights based on available devices and state of the sun. """ """ Class to turn on lights based on available devices and state of the sun. """
def __init__(self, eventbus, statemachine, weather, device_tracker, light_control): def __init__(self, eventbus, statemachine, device_tracker, light_control):
self.eventbus = eventbus self.eventbus = eventbus
self.statemachine = statemachine self.statemachine = statemachine
self.weather = weather
self.light_control = light_control self.light_control = light_control
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
@ -58,7 +58,7 @@ class LightTrigger(object):
We will schedule to have each light start after one another We will schedule to have each light start after one another
and slowly transition in.""" and slowly transition in."""
start_point = self._get_start_point_turn_light_before_sun_set() start_point = self._start_point_turn_light_before_sun_set()
def turn_on(light_id): def turn_on(light_id):
""" Lambda can keep track of function parameters, not from local parameters """ Lambda can keep track of function parameters, not from local parameters
@ -87,7 +87,7 @@ class LightTrigger(object):
if category != STATE_CATEGORY_ALL_DEVICES and new_state.state == DEVICE_STATE_HOME: if category != STATE_CATEGORY_ALL_DEVICES and new_state.state == DEVICE_STATE_HOME:
# These variables are needed for the elif check # These variables are needed for the elif check
now = datetime.now() now = datetime.now()
start_point = self._get_start_point_turn_light_before_sun_set() start_point = self._start_point_turn_light_before_sun_set()
# Do we need lights? # Do we need lights?
if light_needed: if light_needed:
@ -96,7 +96,7 @@ class LightTrigger(object):
# Are we in the time span were we would turn on the lights if someone would be home? # Are we in the time span were we would turn on the lights if someone would be home?
# Check this by seeing if current time is later then the start point # Check this by seeing if current time is later then the start point
elif now > start_point and now < self.weather.next_sun_setting(): elif now > start_point and now < self._next_sun_setting():
# If this is the case check for every light if it would be on # If this is the case check for every light if it would be on
# if someone was home when the fading in started and turn it on # if someone was home when the fading in started and turn it on
@ -116,11 +116,14 @@ class LightTrigger(object):
self.logger.info("Everyone has left but lights are on. Turning lights off") self.logger.info("Everyone has left but lights are on. Turning lights off")
self.light_control.turn_light_off() self.light_control.turn_light_off()
def _next_sun_setting(self):
""" Returns the datetime object representing the next sun setting. """
return dateutil.parser.parse(self.statemachine.get_state(STATE_CATEGORY_NEXT_SUN_SETTING).state)
def _get_start_point_turn_light_before_sun_set(self): def _start_point_turn_light_before_sun_set(self):
""" Helper method to calculate the point in time we have to start fading in lights """ Helper method to calculate the point in time we have to start fading in lights
so that all the lights are on the moment the sun sets. """ so that all the lights are on the moment the sun sets. """
return self.weather.next_sun_setting() - LIGHT_TRANSITION_TIME * len(self.light_control.light_ids) return self._next_sun_setting() - LIGHT_TRANSITION_TIME * len(self.light_control.light_ids)
class HueLightControl(object): class HueLightControl(object):

View File

@ -21,6 +21,8 @@ import ephem
from . import track_time_change from . import track_time_change
STATE_CATEGORY_SUN = "weather.sun" STATE_CATEGORY_SUN = "weather.sun"
STATE_CATEGORY_NEXT_SUN_RISING = "weather.next_sun_rising"
STATE_CATEGORY_NEXT_SUN_SETTING = "weather.next_setting"
STATE_CATEGORY_ALL_DEVICES = 'all_devices' STATE_CATEGORY_ALL_DEVICES = 'all_devices'
STATE_CATEGORY_DEVICE_FORMAT = '{}' STATE_CATEGORY_DEVICE_FORMAT = '{}'
@ -37,46 +39,19 @@ TOMATO_MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
TOMATO_KNOWN_DEVICES_FILE = "tomato_known_devices.csv" TOMATO_KNOWN_DEVICES_FILE = "tomato_known_devices.csv"
class WeatherWatcher(object): def track_sun(eventbus, statemachine, latitude, longitude):
""" Class that keeps track of the state of the sun. """ """ Tracks the state of the sun. """
def __init__(self, eventbus, statemachine, latitude, longitude): sun = ephem.Sun()
self.logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
self.eventbus = eventbus
self.statemachine = statemachine
self.latitude = latitude
self.longitude = longitude
self.sun = ephem.Sun() def update_sun_state(now):
observer = ephem.Observer()
observer.lat = latitude
observer.long = longitude
self._update_sun_state() next_rising = ephem.localtime(observer.next_rising(sun))
next_setting = ephem.localtime(observer.next_setting(sun))
def next_sun_rising(self, observer=None):
""" Returns a datetime object that points at the next sun rising. """
if observer is None:
observer = self._get_observer()
return ephem.localtime(observer.next_rising(self.sun))
def next_sun_setting(self, observer=None):
""" Returns a datetime object that points at the next sun setting. """
if observer is None:
observer = self._get_observer()
return ephem.localtime(observer.next_setting(self.sun))
def _update_sun_state(self, now=None):
""" Updates the state of the sun and schedules when to check next. """
observer = self._get_observer()
next_rising = self.next_sun_rising(observer)
next_setting = self.next_sun_setting(observer)
if next_rising > next_setting: if next_rising > next_setting:
new_state = SUN_STATE_ABOVE_HORIZON new_state = SUN_STATE_ABOVE_HORIZON
@ -86,21 +61,17 @@ class WeatherWatcher(object):
new_state = SUN_STATE_BELOW_HORIZON new_state = SUN_STATE_BELOW_HORIZON
next_change = next_rising next_change = next_rising
self.logger.info("Sun:{}. Next change: {}".format(new_state, next_change.strftime("%H:%M"))) logger.info("Sun:{}. Next change: {}".format(new_state, next_change.strftime("%H:%M")))
self.statemachine.set_state(STATE_CATEGORY_SUN, new_state) statemachine.set_state(STATE_CATEGORY_SUN, new_state)
statemachine.set_state(STATE_CATEGORY_NEXT_SUN_RISING, next_rising.isoformat())
statemachine.set_state(STATE_CATEGORY_NEXT_SUN_SETTING, next_setting.isoformat())
# +10 seconds to be sure that the change has occured # +10 seconds to be sure that the change has occured
track_time_change(self.eventbus, self._update_sun_state, point_in_time=next_change + timedelta(seconds=10)) track_time_change(eventbus, update_sun_state, point_in_time=next_change + timedelta(seconds=10))
update_sun_state(None)
def _get_observer(self):
""" Creates an observer representing the location and the current time. """
observer = ephem.Observer()
observer.lat = self.latitude
observer.long = self.longitude
return observer
class DeviceTracker(object): class DeviceTracker(object):
""" Class that tracks which devices are home and which are not. """ """ Class that tracks which devices are home and which are not. """

View File

@ -2,7 +2,7 @@ from ConfigParser import SafeConfigParser
from homeassistant import StateMachine, EventBus, start_home_assistant from homeassistant import StateMachine, EventBus, start_home_assistant
from homeassistant.observers import TomatoDeviceScanner, DeviceTracker, WeatherWatcher from homeassistant.observers import TomatoDeviceScanner, DeviceTracker, track_sun
from homeassistant.actors import HueLightControl, LightTrigger from homeassistant.actors import HueLightControl, LightTrigger
from homeassistant.httpinterface import HTTPInterface from homeassistant.httpinterface import HTTPInterface
@ -20,12 +20,10 @@ tomato = TomatoDeviceScanner(config.get('tomato','host'), config.get('tomato','u
devicetracker = DeviceTracker(eventbus, statemachine, tomato) devicetracker = DeviceTracker(eventbus, statemachine, tomato)
weatherwatcher = WeatherWatcher(eventbus, statemachine, track_sun(eventbus, statemachine, config.get("common","latitude"), config.get("common","longitude"))
config.get("common","latitude"),
config.get("common","longitude"))
# Init actors # Init actors
LightTrigger(eventbus, statemachine, weatherwatcher, devicetracker, HueLightControl()) LightTrigger(eventbus, statemachine, devicetracker, HueLightControl())
# Init HTTP interface # Init HTTP interface
HTTPInterface(eventbus, statemachine, config.get("common","api_password")) HTTPInterface(eventbus, statemachine, config.get("common","api_password"))