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.{}'
class DeviceTracker:
class DeviceTracker(object):
def __init__(self, eventbus, statemachine, device_scanner):
self.statemachine = statemachine
self.eventbus = eventbus
self.device_scanner = device_scanner
default_last_seen = datetime(1990, 1, 1)
@ -32,8 +31,6 @@ class DeviceTracker:
'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) }
for device in temp_devices_to_track }
self.all_devices_state = STATE_DEVICE_DEFAULT
# Add categories to state machine
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
# not show up for 1 scan beacuse of reboot etc
for device in temp_tracking_devices:
if self.statemachine.get_state(self.devices_to_track[device]['category']).state == STATE_DEVICE_HOME and \
datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
if datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
self.set_state(device, STATE_DEVICE_NOT_HOME)
# 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]
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,11 +6,11 @@ from threading import Thread, RLock
ALL_EVENTS = '*'
class EventBus:
class EventBus(object):
def __init__(self):
self.listeners = defaultdict(list)
self.lock = RLock()
self.logger =logging.getLogger(__name__)
self.logger = logging.getLogger(__name__)
def fire(self, event):
assert isinstance(event, Event), "event needs to be an instance of Event"
@ -21,21 +21,21 @@ class EventBus:
def run():
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)
if event.removeListener:
if event.remove_listener:
if callback in self.listeners[ALL_EVENTS]:
self.listeners[ALL_EVENTS].remove(callback)
if callback in self.listeners[event.eventType]:
self.listeners[event.eventType].remove(callback)
if callback in self.listeners[event.event_type]:
self.listeners[event.event_type].remove(callback)
event.removeListener = False
event.remove_listener = False
if event.stopPropegating:
if event.stop_propegating:
break
self.lock.release()
@ -52,13 +52,12 @@ class EventBus:
self.lock.release()
class Event:
def __init__(self, eventType, data):
self.eventType = eventType
class Event(object):
def __init__(self, event_type, data):
self.event_type = event_type
self.data = data
self.stopPropegating = False
self.removeListener = False
self.stop_propegating = False
self.remove_listener = False
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.EventBus import EventBus
from app.DeviceTracker import DeviceTracker
from HttpInterface import HttpInterface
from app.HttpInterface import HttpInterface
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:
class HomeAssistant(object):
def __init__(self):
self.config = None
@ -90,7 +89,7 @@ class HomeAssistant:
try:
time.sleep(1)
except:
except KeyboardInterrupt:
print ""
print "Interrupt received. Wrapping up and quiting.."
self.timer.stop()
@ -99,8 +98,3 @@ class HomeAssistant:
self.httpinterface.stop()
break

View File

@ -5,7 +5,7 @@ import requests
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
SERVER_HOST= '127.0.0.1'
SERVER_HOST = '127.0.0.1'
SERVER_PORT = 8080
class RequestHandler(BaseHTTPRequestHandler):

View File

@ -1,4 +1,4 @@
from collections import defaultdict, namedtuple
from collections import namedtuple
from threading import RLock
from datetime import datetime
@ -7,29 +7,29 @@ from app.util import ensure_list, matcher
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.eventBus = eventBus
self.eventbus = eventbus
self.lock = RLock()
def add_category(self, category, initialState):
self.states[category] = state(initialState, datetime.now())
def add_category(self, category, initial_state):
self.states[category] = State(initial_state, datetime.now())
def set_state(self, category, newState):
def set_state(self, category, new_state):
self.lock.acquire()
assert category in self.states, "Category does not exist: {}".format(category)
oldState = self.states[category]
old_state = self.states[category]
if oldState.state != newState:
self.states[category] = state(newState, datetime.now())
if old_state.state != new_state:
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()
@ -43,18 +43,17 @@ class StateMachine:
yield category, self.states[category].state, self.states[category].last_changed
def track_state_change(eventBus, category, fromState, toState, action):
fromState = ensure_list(fromState)
toState = ensure_list(toState)
def track_state_change(eventbus, category, from_state, to_state, action):
from_state = ensure_list(from_state)
to_state = ensure_list(to_state)
def listener(event):
assert isinstance(event, Event), "event needs to be of Event type"
if category == event.data['category'] and \
matcher(event.data['oldState'].state, fromState) and \
matcher(event.data['newState'].state, toState):
matcher(event.data['old_state'].state, from_state) and \
matcher(event.data['new_state'].state, to_state):
action(event.data['category'], event.data['oldState'], event.data['newState'])
eventBus.listen(EVENT_STATE_CHANGED, listener)
action(event.data['category'], event.data['old_state'], event.data['new_state'])
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)
class HueTrigger:
class HueTrigger(object):
def __init__(self, config, eventbus, statemachine, device_tracker, weather):
self.eventbus = eventbus
self.statemachine = statemachine
@ -49,7 +49,7 @@ class HueTrigger:
if transitiontime is not None:
command['transitiontime'] = transitiontime
self.bridge.set_light([1,2,3], command)
self.bridge.set_light([1, 2, 3], command)
def turn_lights_off(self, transitiontime=None):
@ -58,12 +58,12 @@ class HueTrigger:
if transitiontime is not None:
command['transitiontime'] = transitiontime
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
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
@ -80,17 +80,15 @@ class HueTrigger:
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()
# 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.turn_lights_on()
# 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.turn_lights_off()

View File

@ -1,4 +1,4 @@
from datetime import datetime, timedelta
from datetime import datetime
import threading
import time
@ -39,15 +39,15 @@ class Timer(threading.Thread):
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)
hour, minute, second = ensure_list(hour), ensure_list(minute), ensure_list(second)
def listener(event):
assert isinstance(event, Event), "event needs to be of Event type"
if (datetime is not None and event.data['now'] > datetime) or \
datetime is None and \
if (point_in_time is not None and event.data['now'] > point_in_time) or \
point_in_time is None and \
matcher(event.data['now'].year, year) and \
matcher(event.data['now'].month, month) 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'].second, second):
# datetime are exact points in time so we always remove it after fire
event.removeListener = listen_once or datetime is not None
# point_in_time are exact points in time so we always remove it after fire
event.remove_listener = listen_once or point_in_time is not None
action(event.data['now'])
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"
class TomatoDeviceScanner:
class TomatoDeviceScanner(object):
# self.logger
def __init__(self, config):
@ -39,7 +39,8 @@ class TomatoDeviceScanner:
# Create a dict with ID: NAME of the devices to track
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']
# 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]
except:
except Exception:
self.logger.exception("Scanning failed")
@ -72,11 +73,11 @@ class TomatoDeviceScanner:
def tomato_request(self, action):
# 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},
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
from datetime import datetime, timedelta
from datetime import timedelta
import ephem
from app.EventBus import Event
from app.observer.Timer import track_time_change
STATE_CATEGORY_SUN = "weather.sun"
@ -12,7 +10,7 @@ STATE_CATEGORY_SUN = "weather.sun"
SUN_STATE_ABOVE_HORIZON = "above_horizon"
SUN_STATE_BELOW_HORIZON = "below_horizon"
class WeatherWatcher:
class WeatherWatcher(object):
def __init__(self, config, eventbus, statemachine):
self.logger = logging.getLogger(__name__)
self.config = config
@ -52,6 +50,4 @@ class WeatherWatcher:
self.statemachine.set_state(STATE_CATEGORY_SUN, new_state)
# +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))