diff --git a/app/Dependencies.py b/app/Dependencies.py deleted file mode 100644 index 1cfe7b75887..00000000000 --- a/app/Dependencies.py +++ /dev/null @@ -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 - diff --git a/app/observer/DeviceTracker.py b/app/DeviceTracker.py similarity index 100% rename from app/observer/DeviceTracker.py rename to app/DeviceTracker.py diff --git a/app/EventBus.py b/app/EventBus.py index bb48a30ffb9..4bb3807e054 100644 --- a/app/EventBus.py +++ b/app/EventBus.py @@ -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) diff --git a/app/HomeAssistant.py b/app/HomeAssistant.py new file mode 100644 index 00000000000..1150761496f --- /dev/null +++ b/app/HomeAssistant.py @@ -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 + + + + + diff --git a/app/Logging.py b/app/Logging.py deleted file mode 100644 index 123fb31e61b..00000000000 --- a/app/Logging.py +++ /dev/null @@ -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)) \ No newline at end of file diff --git a/app/actor/HueTrigger.py b/app/actor/HueTrigger.py index ff97f03d796..6695b968b21 100644 --- a/app/actor/HueTrigger.py +++ b/app/actor/HueTrigger.py @@ -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() diff --git a/app/observer/TomatoDeviceScanner.py b/app/observer/TomatoDeviceScanner.py index df226d4de84..e504cc70166 100644 --- a/app/observer/TomatoDeviceScanner.py +++ b/app/observer/TomatoDeviceScanner.py @@ -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 diff --git a/app/util.py b/app/util.py index 4311737aa21..3adac6ea053 100644 --- a/app/util.py +++ b/app/util.py @@ -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 \ No newline at end of file + return '*' in pattern or subject in pattern diff --git a/start.py b/start.py index d86dc109d4a..dc081968c0e 100644 --- a/start.py +++ b/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 \ No newline at end of file +ha.start()