Reorganizing the code + sun set puts lights on
parent
37e5840173
commit
b20bd6c9c0
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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))
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
from EventBus import ALL_EVENTS
|
||||
|
||||
def ensure_list(parameter):
|
||||
return parameter if isinstance(parameter, list) else [parameter]
|
||||
|
||||
def matcher(subject, pattern):
|
||||
return '*' in pattern or subject in pattern
|
||||
return '*' in pattern or subject in pattern
|
||||
|
|
35
start.py
35
start.py
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue