Reorganizing the code + sun set puts lights on

pull/2/head
Paulus Schoutsen 2013-09-17 15:13:20 -07:00
parent 37e5840173
commit b20bd6c9c0
9 changed files with 150 additions and 95 deletions

View File

@ -1,34 +0,0 @@
from ConfigParser import SafeConfigParser
from app.StateMachine import StateMachine
from app.EventBus import EventBus
from app.Logging import EventLogger
class Dependencies:
def __init__(self):
self.config = None
self.eventbus = None
self.statemachine = None
def get_config(self):
if self.config is None:
self.config = SafeConfigParser()
self.config.read("home-assistant.conf")
return self.config
def get_event_bus(self):
if self.eventbus is None:
self.eventbus = EventBus()
#EventLogger(self.eventbus)
return self.eventbus
def get_state_machine(self):
if self.statemachine is None:
self.statemachine = StateMachine(self.get_event_bus())
return self.statemachine

View File

@ -1,5 +1,5 @@
import logging
import time
from collections import defaultdict
from itertools import chain
from threading import Thread, RLock
@ -19,7 +19,7 @@ class EventBus:
def run():
self.lock.acquire()
self.logger.info("[{}] {} event received: {}".format(time.strftime("%H:%M:%S"), event.eventType, event.data))
self.logger.info("{} event received: {}".format(event.eventType, event.data))
for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.eventType]):
callback(event)

96
app/HomeAssistant.py Normal file
View File

@ -0,0 +1,96 @@
from ConfigParser import SafeConfigParser
import time
from app.StateMachine import StateMachine
from app.EventBus import EventBus
from app.Logging import EventLogger
from app.DeviceTracker import DeviceTracker
from app.observer.WeatherWatcher import WeatherWatcher
from app.observer.TomatoDeviceScanner import TomatoDeviceScanner
from app.observer.Timer import Timer
from app.actor.HueTrigger import HueTrigger
class HomeAssistant:
def __init__(self):
self.config = None
self.eventbus = None
self.statemachine = None
self.timer = None
self.weatherwatcher = None
self.devicetracker = None
self.huetrigger = None
def get_config(self):
if self.config is None:
self.config = SafeConfigParser()
self.config.read("home-assistant.conf")
return self.config
def get_event_bus(self):
if self.eventbus is None:
self.eventbus = EventBus()
return self.eventbus
def get_state_machine(self):
if self.statemachine is None:
self.statemachine = StateMachine(self.get_event_bus())
return self.statemachine
def setup_timer(self):
if self.timer is None:
self.timer = Timer(self.get_event_bus())
return self.timer
def setup_weather_watcher(self):
if self.weatherwatcher is None:
self.weatherwatcher = WeatherWatcher(self.get_config(), self.get_event_bus(), self.get_state_machine())
return self.weatherwatcher
def setup_device_tracker(self, device_scanner):
if self.devicetracker is None:
self.devicetracker = DeviceTracker(self.get_event_bus(), self.get_state_machine(), device_scanner)
return self.devicetracker
def setup_hue_trigger(self):
if self.huetrigger is None:
assert self.devicetracker is not None, "Cannot setup Hue Trigger without a device tracker being setup"
self.huetrigger = HueTrigger(self.get_config(), self.get_event_bus(), self.get_state_machine(), self.devicetracker)
return self.huetrigger
def start(self):
self.setup_timer().start()
while True:
try:
time.sleep(1)
except:
print ""
print "Interrupt received. Wrapping up and quiting.."
self.timer.stop()
break

View File

@ -1,14 +0,0 @@
import time
import logging
from app.EventBus import ALL_EVENTS
class EventLogger:
def __init__(self, eventbus):
eventbus.listen(ALL_EVENTS, self.log)
self.logger = logging.getLogger("EventLogger")
def log(self, event):
self.logger.info("[{}] {} event received: {}".format(time.strftime("%H:%M:%S"), event.eventType, event.data))

View File

@ -2,7 +2,7 @@ import logging
from phue import Bridge
from app.observer.WeatherWatcher import STATE_CATEGORY_SUN, SOLAR_STATE_BELOW_HORIZON
from app.observer.WeatherWatcher import STATE_CATEGORY_SUN, SOLAR_STATE_BELOW_HORIZON, SOLAR_STATE_ABOVE_HORIZON
from app.StateMachine import track_state_change
from app.observer.DeviceTracker import STATE_CATEGORY_ALL_DEVICES, STATE_H, STATE_H5, STATE_NH
@ -14,25 +14,51 @@ class HueTrigger:
self.logger = logging.getLogger("HueTrigger")
for category in device_tracker.device_state_categories():
track_state_change(eventbus, category, '*', STATE_H, self.handle_state_change)
track_state_change(eventbus, category, '*', STATE_H, self.handle_device_state_change)
# Track when all devices are gone to shut down lights
track_state_change(eventbus, STATE_CATEGORY_ALL_DEVICES, [STATE_H,STATE_H5], STATE_NH, self.handle_state_change)
track_state_change(eventbus, STATE_CATEGORY_ALL_DEVICES, [STATE_H,STATE_H5], STATE_NH, self.handle_device_state_change)
def handle_state_change(self, category, oldState, newState):
# print "Hue Trigger - {}: {}->{}".format(category, oldState, newState)
# Track when sun sets
track_state_change(eventbus, STATE_CATEGORY_SUN, SOLAR_STATE_ABOVE_HORIZON, SOLAR_STATE_BELOW_HORIZON, self.handle_sun_state_change)
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) == SOLAR_STATE_BELOW_HORIZON
if newState == STATE_H and light_needed:
return lights_are_on, light_needed
def turn_lights_on(self):
self.bridge.set_light([1,2,3], 'on', True)
self.bridge.set_light([1,2,3], 'xy', [0.4595, 0.4105])
def turn_lights_off(self):
self.bridge.set_light([1,2,3], 'on', False)
# If sun sets, the lights are
def handle_sun_state_change(self, category, oldState, newState):
lights_are_on, light_needed = self.get_lights_status()
if light_needed and self.statemachine.get_state(STATE_CATEGORY_ALL_DEVICES) in [STATE_H, STATE_H5]:
self.turn_lights_on()
def handle_device_state_change(self, category, oldState, newState):
lights_are_on, light_needed = self.get_lights_status()
# Specific device came home ?
if category != STATE_CATEGORY_ALL_DEVICES and newState == STATE_H and light_needed:
self.logger.info("Home coming event for {}. Turning lights on".format(category))
self.bridge.set_light([1,2,3], 'on', True)
self.bridge.set_light([1,2,3], 'xy', [0.4595, 0.4105])
self.turn_lights_on()
elif newState == STATE_NH and lights_are_on:
# Did all devices leave the house?
elif category == STATE_CATEGORY_ALL_DEVICES and newState == STATE_NH and lights_are_on:
self.logger.info("Everyone has left. Turning lights off")
self.bridge.set_light([1,2,3], 'on', False)
self.turn_lights_off()

View File

@ -31,7 +31,8 @@ class TomatoDeviceScanner:
for mac in [mac for mac in known_devices if known_devices[mac]['track'] == '1']:
self.devices_to_track[mac] = known_devices[mac]['name']
# Doesn't go together with exec: unqualified exec is not allowed in function '__init__' it contains a nested function with free variables
# Quicker way of the previous statement but it doesn't go together with exec:
# unqualified exec is not allowed in function '__init__' it contains a nested function with free variables
# self.devices_to_track = {mac: known_devices[mac]['name'] for mac in known_devices if known_devices[mac]['track'] == '1'}
@ -42,10 +43,15 @@ class TomatoDeviceScanner:
self.logger.info("Scanning for new devices")
# Query for new devices
exec(self.tomato_request("devlist"))
try:
exec(self.tomato_request("devlist"))
return [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
return [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
except:
self.logger.error("Scanning failed")
return []
def tomato_request(self, action):
# Get router info

View File

@ -1,5 +1,3 @@
from EventBus import ALL_EVENTS
def ensure_list(parameter):
return parameter if isinstance(parameter, list) else [parameter]

View File

@ -1,34 +1,11 @@
import time
from app.HomeAssistant import HomeAssistant
from app.Dependencies import Dependencies
from app.observer.WeatherWatcher import WeatherWatcher
from app.observer.DeviceTracker import DeviceTracker
from app.observer.TomatoDeviceScanner import TomatoDeviceScanner
from app.observer.Timer import Timer
from app.actor.HueTrigger import HueTrigger
ha = HomeAssistant()
deps = Dependencies()
ha.setup_weather_watcher()
ha.setup_device_tracker(TomatoDeviceScanner(ha.get_config()))
ha.setup_hue_trigger()
weather = WeatherWatcher(deps.get_config(), deps.get_event_bus(), deps.get_state_machine())
tomato = TomatoDeviceScanner(deps.get_config())
device_tracker = DeviceTracker(deps.get_event_bus(), deps.get_state_machine(), tomato)
HueTrigger(deps.get_config(), deps.get_event_bus(), deps.get_state_machine(), device_tracker)
timer = Timer(deps.get_event_bus())
timer.start()
while True:
try:
time.sleep(1)
except:
print ""
print "Interrupt received. Wrapping up and quiting.."
timer.stop()
break
ha.start()