Cleaning up code

This commit is contained in:
Paulus Schoutsen 2013-09-20 10:00:31 -07:00
parent 90c343d16b
commit 7636092c7d
10 changed files with 403 additions and 423 deletions

View File

@ -16,12 +16,11 @@ STATE_CATEGORY_ALL_DEVICES = 'device.alldevices'
STATE_CATEGORY_DEVICE_FORMAT = 'device.{}' STATE_CATEGORY_DEVICE_FORMAT = 'device.{}'
class DeviceTracker: class DeviceTracker(object):
def __init__(self, eventbus, statemachine, device_scanner): def __init__(self, eventbus, statemachine, device_scanner):
self.statemachine = statemachine self.statemachine = statemachine
self.eventbus = eventbus self.eventbus = eventbus
self.device_scanner = device_scanner
default_last_seen = datetime(1990, 1, 1) default_last_seen = datetime(1990, 1, 1)
@ -32,8 +31,6 @@ class DeviceTracker:
'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) } 'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) }
for device in temp_devices_to_track } for device in temp_devices_to_track }
self.all_devices_state = STATE_DEVICE_DEFAULT
# Add categories to state machine # Add categories to state machine
statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT) statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT)
@ -71,15 +68,12 @@ class DeviceTracker:
# Because we do not want to have stuff happening when the device does # Because we do not want to have stuff happening when the device does
# not show up for 1 scan beacuse of reboot etc # not show up for 1 scan beacuse of reboot etc
for device in temp_tracking_devices: for device in temp_tracking_devices:
if self.statemachine.get_state(self.devices_to_track[device]['category']).state == STATE_DEVICE_HOME and \ if datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
self.set_state(device, STATE_DEVICE_NOT_HOME) self.set_state(device, STATE_DEVICE_NOT_HOME)
# Get the set of currently used statuses # Get the set of currently used statuses
states_of_devices = [self.statemachine.get_state(self.devices_to_track[device]['category']).state for device in self.devices_to_track] states_of_devices = [self.statemachine.get_state(self.devices_to_track[device]['category']).state for device in self.devices_to_track]
self.all_devices_state = STATE_DEVICE_HOME if STATE_DEVICE_HOME in states_of_devices else STATE_DEVICE_NOT_HOME all_devices_state = STATE_DEVICE_HOME if STATE_DEVICE_HOME in states_of_devices else STATE_DEVICE_NOT_HOME
self.statemachine.set_state(STATE_CATEGORY_ALL_DEVICES, self.all_devices_state) self.statemachine.set_state(STATE_CATEGORY_ALL_DEVICES, all_devices_state)

View File

@ -6,7 +6,7 @@ from threading import Thread, RLock
ALL_EVENTS = '*' ALL_EVENTS = '*'
class EventBus: class EventBus(object):
def __init__(self): def __init__(self):
self.listeners = defaultdict(list) self.listeners = defaultdict(list)
self.lock = RLock() self.lock = RLock()
@ -21,21 +21,21 @@ class EventBus:
def run(): def run():
self.lock.acquire() self.lock.acquire()
self.logger.info("{} event received: {}".format(event.eventType, event.data)) self.logger.info("{} event received: {}".format(event.event_type, event.data))
for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.eventType]): for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.event_type]):
callback(event) callback(event)
if event.removeListener: if event.remove_listener:
if callback in self.listeners[ALL_EVENTS]: if callback in self.listeners[ALL_EVENTS]:
self.listeners[ALL_EVENTS].remove(callback) self.listeners[ALL_EVENTS].remove(callback)
if callback in self.listeners[event.eventType]: if callback in self.listeners[event.event_type]:
self.listeners[event.eventType].remove(callback) self.listeners[event.event_type].remove(callback)
event.removeListener = False event.remove_listener = False
if event.stopPropegating: if event.stop_propegating:
break break
self.lock.release() self.lock.release()
@ -52,13 +52,12 @@ class EventBus:
self.lock.release() self.lock.release()
class Event: class Event(object):
def __init__(self, eventType, data): def __init__(self, event_type, data):
self.eventType = eventType self.event_type = event_type
self.data = data self.data = data
self.stopPropegating = False self.stop_propegating = False
self.removeListener = False self.remove_listener = False
def __str__(self): def __str__(self):
return str([self.eventType, self.data]) return str([self.event_type, self.data])

View File

@ -4,15 +4,14 @@ import time
from app.StateMachine import StateMachine from app.StateMachine import StateMachine
from app.EventBus import EventBus from app.EventBus import EventBus
from app.DeviceTracker import DeviceTracker from app.DeviceTracker import DeviceTracker
from HttpInterface import HttpInterface from app.HttpInterface import HttpInterface
from app.observer.WeatherWatcher import WeatherWatcher from app.observer.WeatherWatcher import WeatherWatcher
from app.observer.TomatoDeviceScanner import TomatoDeviceScanner
from app.observer.Timer import Timer from app.observer.Timer import Timer
from app.actor.HueTrigger import HueTrigger from app.actor.HueTrigger import HueTrigger
class HomeAssistant: class HomeAssistant(object):
def __init__(self): def __init__(self):
self.config = None self.config = None
@ -90,7 +89,7 @@ class HomeAssistant:
try: try:
time.sleep(1) time.sleep(1)
except: except KeyboardInterrupt:
print "" print ""
print "Interrupt received. Wrapping up and quiting.." print "Interrupt received. Wrapping up and quiting.."
self.timer.stop() self.timer.stop()
@ -99,8 +98,3 @@ class HomeAssistant:
self.httpinterface.stop() self.httpinterface.stop()
break break

View File

@ -1,4 +1,4 @@
from collections import defaultdict, namedtuple from collections import namedtuple
from threading import RLock from threading import RLock
from datetime import datetime from datetime import datetime
@ -7,29 +7,29 @@ from app.util import ensure_list, matcher
EVENT_STATE_CHANGED = "state_changed" EVENT_STATE_CHANGED = "state_changed"
state = namedtuple("State", ['state','last_changed']) State = namedtuple("State", ['state','last_changed'])
class StateMachine: class StateMachine(object):
def __init__(self, eventBus): def __init__(self, eventbus):
self.states = dict() self.states = dict()
self.eventBus = eventBus self.eventbus = eventbus
self.lock = RLock() self.lock = RLock()
def add_category(self, category, initialState): def add_category(self, category, initial_state):
self.states[category] = state(initialState, datetime.now()) self.states[category] = State(initial_state, datetime.now())
def set_state(self, category, newState): def set_state(self, category, new_state):
self.lock.acquire() self.lock.acquire()
assert category in self.states, "Category does not exist: {}".format(category) assert category in self.states, "Category does not exist: {}".format(category)
oldState = self.states[category] old_state = self.states[category]
if oldState.state != newState: if old_state.state != new_state:
self.states[category] = state(newState, datetime.now()) self.states[category] = State(new_state, datetime.now())
self.eventBus.fire(Event(EVENT_STATE_CHANGED, {'category':category, 'oldState':oldState, 'newState':self.states[category]})) self.eventbus.fire(Event(EVENT_STATE_CHANGED, {'category':category, 'old_state':old_state, 'new_state':self.states[category]}))
self.lock.release() self.lock.release()
@ -43,18 +43,17 @@ class StateMachine:
yield category, self.states[category].state, self.states[category].last_changed yield category, self.states[category].state, self.states[category].last_changed
def track_state_change(eventBus, category, fromState, toState, action): def track_state_change(eventbus, category, from_state, to_state, action):
fromState = ensure_list(fromState) from_state = ensure_list(from_state)
toState = ensure_list(toState) to_state = ensure_list(to_state)
def listener(event): def listener(event):
assert isinstance(event, Event), "event needs to be of Event type" assert isinstance(event, Event), "event needs to be of Event type"
if category == event.data['category'] and \ if category == event.data['category'] and \
matcher(event.data['oldState'].state, fromState) and \ matcher(event.data['old_state'].state, from_state) and \
matcher(event.data['newState'].state, toState): matcher(event.data['new_state'].state, to_state):
action(event.data['category'], event.data['oldState'], event.data['newState']) action(event.data['category'], event.data['old_state'], event.data['new_state'])
eventBus.listen(EVENT_STATE_CHANGED, listener)
eventbus.listen(EVENT_STATE_CHANGED, listener)

View File

@ -10,7 +10,7 @@ from app.observer.Timer import track_time_change
LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=30) LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=30)
class HueTrigger: class HueTrigger(object):
def __init__(self, config, eventbus, statemachine, device_tracker, weather): def __init__(self, config, eventbus, statemachine, device_tracker, weather):
self.eventbus = eventbus self.eventbus = eventbus
self.statemachine = statemachine self.statemachine = statemachine
@ -61,9 +61,9 @@ class HueTrigger:
self.bridge.set_light([1, 2, 3], command) self.bridge.set_light([1, 2, 3], command)
def handle_sun_rising(self, category, oldState, newState): def handle_sun_rising(self, category, old_state, new_state):
# Schedule an event X minutes prior to sun setting # Schedule an event X minutes prior to sun setting
track_time_change(self.eventbus, self.handle_sun_setting, datetime=self.weather.next_sun_setting()-LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD) track_time_change(self.eventbus, self.handle_sun_setting, point_in_time=self.weather.next_sun_setting()-LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD)
# Gets called when darkness starts falling in, slowly turn on the lights # Gets called when darkness starts falling in, slowly turn on the lights
@ -80,17 +80,15 @@ class HueTrigger:
self.turn_lights_on(transitiontime) self.turn_lights_on(transitiontime)
def handle_device_state_change(self, category, oldState, newState): def handle_device_state_change(self, category, old_state, new_state):
lights_are_on, light_needed = self.get_lights_status() lights_are_on, light_needed = self.get_lights_status()
# Specific device came home ? # Specific device came home ?
if category != STATE_CATEGORY_ALL_DEVICES and newState.state == STATE_DEVICE_HOME and light_needed: 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.logger.info("Home coming event for {}. Turning lights on".format(category))
self.turn_lights_on() self.turn_lights_on()
# Did all devices leave the house? # Did all devices leave the house?
elif category == STATE_CATEGORY_ALL_DEVICES and newState.state == STATE_DEVICE_NOT_HOME and lights_are_on: 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.logger.info("Everyone has left. Turning lights off")
self.turn_lights_off() self.turn_lights_off()

View File

@ -1,4 +1,4 @@
from datetime import datetime, timedelta from datetime import datetime
import threading import threading
import time import time
@ -39,15 +39,15 @@ class Timer(threading.Thread):
break break
def track_time_change(eventBus, action, year='*', month='*', day='*', hour='*', minute='*', second='*', datetime=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) year, month, day = ensure_list(year), ensure_list(month), ensure_list(day)
hour, minute, second = ensure_list(hour), ensure_list(minute), ensure_list(second) hour, minute, second = ensure_list(hour), ensure_list(minute), ensure_list(second)
def listener(event): def listener(event):
assert isinstance(event, Event), "event needs to be of Event type" assert isinstance(event, Event), "event needs to be of Event type"
if (datetime is not None and event.data['now'] > datetime) or \ if (point_in_time is not None and event.data['now'] > point_in_time) or \
datetime is None and \ point_in_time is None and \
matcher(event.data['now'].year, year) and \ matcher(event.data['now'].year, year) and \
matcher(event.data['now'].month, month) and \ matcher(event.data['now'].month, month) and \
matcher(event.data['now'].day, day) and \ matcher(event.data['now'].day, day) and \
@ -55,10 +55,9 @@ def track_time_change(eventBus, action, year='*', month='*', day='*', hour='*',
matcher(event.data['now'].minute, minute) and \ matcher(event.data['now'].minute, minute) and \
matcher(event.data['now'].second, second): matcher(event.data['now'].second, second):
# datetime are exact points in time so we always remove it after fire # point_in_time are exact points in time so we always remove it after fire
event.removeListener = listen_once or datetime is not None event.remove_listener = listen_once or point_in_time is not None
action(event.data['now']) action(event.data['now'])
eventBus.listen(EVENT_TIME_CHANGED, listener) eventBus.listen(EVENT_TIME_CHANGED, listener)

View File

@ -10,7 +10,7 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
KNOWN_DEVICES_FILE = "tomato_known_devices.csv" KNOWN_DEVICES_FILE = "tomato_known_devices.csv"
class TomatoDeviceScanner: class TomatoDeviceScanner(object):
# self.logger # self.logger
def __init__(self, config): def __init__(self, config):
@ -39,7 +39,8 @@ class TomatoDeviceScanner:
# Create a dict with ID: NAME of the devices to track # Create a dict with ID: NAME of the devices to track
self.devices_to_track = dict() self.devices_to_track = dict()
for mac in [mac for mac in known_devices if known_devices[mac]['track'] == '1']: for mac in known_devices:
if known_devices[mac]['track'] == '1':
self.devices_to_track[mac] = known_devices[mac]['name'] self.devices_to_track[mac] = known_devices[mac]['name']
# Quicker way of the previous statement but it doesn't go together with exec: # Quicker way of the previous statement but it doesn't go together with exec:
@ -63,7 +64,7 @@ class TomatoDeviceScanner:
self.last_results = [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev] self.last_results = [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
except: except Exception:
self.logger.exception("Scanning failed") self.logger.exception("Scanning failed")
@ -72,11 +73,11 @@ class TomatoDeviceScanner:
def tomato_request(self, action): def tomato_request(self, action):
# Get router info # Get router info
r = requests.post('http://{}/update.cgi'.format(self.config.get('tomato','host')), req = requests.post('http://{}/update.cgi'.format(self.config.get('tomato','host')),
data={'_http_id':self.config.get('tomato','http_id'), 'exec':action}, data={'_http_id':self.config.get('tomato','http_id'), 'exec':action},
auth=requests.auth.HTTPBasicAuth(self.config.get('tomato','username'), self.config.get('tomato','password'))) auth=requests.auth.HTTPBasicAuth(self.config.get('tomato','username'), self.config.get('tomato','password')))
return r.text return req.text

View File

@ -1,10 +1,8 @@
import logging import logging
from datetime import datetime, timedelta from datetime import timedelta
import ephem import ephem
from app.EventBus import Event
from app.observer.Timer import track_time_change from app.observer.Timer import track_time_change
STATE_CATEGORY_SUN = "weather.sun" STATE_CATEGORY_SUN = "weather.sun"
@ -12,7 +10,7 @@ STATE_CATEGORY_SUN = "weather.sun"
SUN_STATE_ABOVE_HORIZON = "above_horizon" SUN_STATE_ABOVE_HORIZON = "above_horizon"
SUN_STATE_BELOW_HORIZON = "below_horizon" SUN_STATE_BELOW_HORIZON = "below_horizon"
class WeatherWatcher: class WeatherWatcher(object):
def __init__(self, config, eventbus, statemachine): def __init__(self, config, eventbus, statemachine):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.config = config self.config = config
@ -52,6 +50,4 @@ class WeatherWatcher:
self.statemachine.set_state(STATE_CATEGORY_SUN, new_state) self.statemachine.set_state(STATE_CATEGORY_SUN, new_state)
# +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, datetime=next_change + timedelta(seconds=10)) track_time_change(self.eventbus, self.update_sun_state, point_in_time=next_change + timedelta(seconds=10))