Added HttpInterface
This commit is contained in:
parent
cfc6708283
commit
361a935591
6 changed files with 117 additions and 22 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
89
app/HttpInterface.py
Normal 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))
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
1
start.py
1
start.py
|
@ -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()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue