Added HttpInterface

This commit is contained in:
Paulus Schoutsen 2013-09-19 23:59:49 -07:00
parent cfc6708283
commit 361a935591
6 changed files with 117 additions and 22 deletions

View file

@ -24,14 +24,12 @@ class DeviceTracker:
self.device_scanner = device_scanner self.device_scanner = device_scanner
default_last_seen = datetime(1990, 1, 1) default_last_seen = datetime(1990, 1, 1)
now = datetime.now()
temp_devices_to_track = device_scanner.get_devices_to_track() temp_devices_to_track = device_scanner.get_devices_to_track()
self.devices_to_track = { device: { 'name': temp_devices_to_track[device], self.devices_to_track = { device: { 'name': temp_devices_to_track[device],
'state': STATE_DEVICE_DEFAULT,
'last_seen': default_last_seen, 'last_seen': default_last_seen,
'state_changed': now } '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 self.all_devices_state = STATE_DEVICE_DEFAULT
@ -40,30 +38,21 @@ class DeviceTracker:
statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT) statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT)
for device in self.devices_to_track: for device in self.devices_to_track:
self.statemachine.add_category(STATE_CATEGORY_DEVICE_FORMAT.format(self.devices_to_track[device]['name']), STATE_DEVICE_DEFAULT) self.statemachine.add_category(self.devices_to_track[device]['category'], STATE_DEVICE_DEFAULT)
track_time_change(eventbus, lambda time: self.update_devices(device_scanner.scan_devices())) track_time_change(eventbus, lambda time: self.update_devices(device_scanner.scan_devices()))
def device_state_categories(self): def device_state_categories(self):
for device in self.devices_to_track: for device in self.devices_to_track:
yield STATE_CATEGORY_DEVICE_FORMAT.format(self.devices_to_track[device]['name']) yield self.devices_to_track[device]['category']
def set_state(self, device, state): def set_state(self, device, state):
now = datetime.now()
if state == STATE_DEVICE_HOME: if state == STATE_DEVICE_HOME:
self.devices_to_track[device]['last_seen'] = now self.devices_to_track[device]['last_seen'] = datetime.now()
if self.devices_to_track[device]['state'] != state: self.statemachine.set_state(self.devices_to_track[device]['category'], state)
self.devices_to_track[device]['state'] = state
self.devices_to_track[device]['state_changed'] = now
self.statemachine.set_state(STATE_CATEGORY_DEVICE_FORMAT.format(self.devices_to_track[device]['name']), state)
def update_devices(self, found_devices): def update_devices(self, found_devices):
@ -89,7 +78,7 @@ class DeviceTracker:
# Get the set of currently used statuses # Get the set of currently used statuses
states_of_devices = [self.devices_to_track[device]['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 self.all_devices_state = STATE_DEVICE_HOME if STATE_DEVICE_HOME in states_of_devices else STATE_DEVICE_NOT_HOME

View file

@ -4,6 +4,7 @@ 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.observer.WeatherWatcher import WeatherWatcher from app.observer.WeatherWatcher import WeatherWatcher
from app.observer.TomatoDeviceScanner import TomatoDeviceScanner from app.observer.TomatoDeviceScanner import TomatoDeviceScanner
@ -23,7 +24,7 @@ class HomeAssistant:
self.devicetracker = None self.devicetracker = None
self.huetrigger = None self.huetrigger = None
self.httpinterface = None
def get_config(self): def get_config(self):
if self.config is None: if self.config is None:
@ -76,6 +77,12 @@ class HomeAssistant:
return self.huetrigger return self.huetrigger
def setup_http_interface(self):
self.httpinterface = HttpInterface(self.get_event_bus(), self.get_state_machine())
self.httpinterface.start()
return self.httpinterface
def start(self): def start(self):
self.setup_timer().start() self.setup_timer().start()
@ -87,6 +94,10 @@ class HomeAssistant:
print "" print ""
print "Interrupt received. Wrapping up and quiting.." print "Interrupt received. Wrapping up and quiting.."
self.timer.stop() self.timer.stop()
if self.httpinterface is not None:
self.httpinterface.stop()
break break

89
app/HttpInterface.py Normal file
View file

@ -0,0 +1,89 @@
import threading
import urlparse
import requests
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
SERVER_HOST= '127.0.0.1'
SERVER_PORT = 8080
class RequestHandler(BaseHTTPRequestHandler):
#Handler for the GET requests
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
write = self.wfile.write
# Describe state machine:
categories = []
write("<table>")
write("<tr><th>Name</th><th>State</th><th>Last Changed</th></tr>")
for category, state, last_changed in self.server.statemachine.get_states():
categories.append(category)
write("<tr><td>{}</td><td>{}</td><td>{}</td></tr>".format(category, state, last_changed.strftime("%H:%M:%S %d-%m-%Y")))
write("</table>")
# Small form to change the state
write("<br />Change state:<br />")
write("<form action='change_state' method='POST'>")
write("<select name='category'>")
for category in categories:
write("<option>{}</option>".format(category))
write("</select>")
write("<input name='new_state' />")
write("<input type='submit' value='set state' />")
write("</form>")
else:
self.send_response(404)
def do_POST(self):
length = int(self.headers['Content-Length'])
post_data = urlparse.parse_qs(self.rfile.read(length))
if self.path == "/change_state":
self.server.statemachine.set_state(post_data['category'][0], post_data['new_state'][0])
self.send_response(301)
self.send_header("Location", "/")
self.end_headers()
else:
self.send_response(404)
class HttpInterface(threading.Thread):
def __init__(self, eventbus, statemachine):
threading.Thread.__init__(self)
self.server = HTTPServer((SERVER_HOST, SERVER_PORT), RequestHandler)
self.server.eventbus = eventbus
self.server.statemachine = statemachine
self._stop = threading.Event()
def run(self):
while not self._stop.is_set():
self.server.handle_request()
def stop(self):
self._stop.set()
# Trigger a fake request to get the server to quit
requests.get("http://{}:{}".format(SERVER_HOST, SERVER_PORT))

View file

@ -38,6 +38,10 @@ class StateMachine:
return self.states[category] return self.states[category]
def get_states(self):
for category in sorted(self.states.keys()):
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, fromState, toState, action):
fromState = ensure_list(fromState) fromState = ensure_list(fromState)

View file

@ -8,7 +8,7 @@ from app.StateMachine import track_state_change
from app.DeviceTracker import STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_HOME, STATE_DEVICE_NOT_HOME from app.DeviceTracker import STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_HOME, STATE_DEVICE_NOT_HOME
from app.observer.Timer import track_time_change from app.observer.Timer import track_time_change
LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=20) LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=30)
class HueTrigger: class HueTrigger:
def __init__(self, config, eventbus, statemachine, device_tracker, weather): def __init__(self, config, eventbus, statemachine, device_tracker, weather):
@ -31,8 +31,9 @@ class HueTrigger:
track_state_change(eventbus, STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, self.handle_sun_rising) track_state_change(eventbus, STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, self.handle_sun_rising)
# If the sun is already above horizon schedule the time-based pre-sun set event # If the sun is already above horizon schedule the time-based pre-sun set event
if True or statemachine.get_state(STATE_CATEGORY_SUN) == SUN_STATE_ABOVE_HORIZON: if statemachine.get_state(STATE_CATEGORY_SUN) == SUN_STATE_ABOVE_HORIZON:
self.handle_sun_rising() self.handle_sun_rising(None, None, None)
def get_lights_status(self): def get_lights_status(self):
lights_are_on = sum([1 for light in self.lights if light.on]) > 0 lights_are_on = sum([1 for light in self.lights if light.on]) > 0
@ -60,7 +61,7 @@ class HueTrigger:
self.bridge.set_light([1,2,3], command) self.bridge.set_light([1,2,3], command)
def handle_sun_rising(self, event=None): def handle_sun_rising(self, category, oldState, newState):
# 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, datetime=self.weather.next_sun_setting()-LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD)

View file

@ -6,5 +6,6 @@ ha = HomeAssistant()
ha.setup_device_tracker(TomatoDeviceScanner(ha.get_config())) ha.setup_device_tracker(TomatoDeviceScanner(ha.get_config()))
ha.setup_hue_trigger() ha.setup_hue_trigger()
ha.setup_http_interface()
ha.start() ha.start()