diff --git a/app/DeviceTracker.py b/app/DeviceTracker.py index 1e1514e8b0f..615acf0a934 100644 --- a/app/DeviceTracker.py +++ b/app/DeviceTracker.py @@ -24,14 +24,12 @@ class DeviceTracker: self.device_scanner = device_scanner default_last_seen = datetime(1990, 1, 1) - now = datetime.now() temp_devices_to_track = device_scanner.get_devices_to_track() self.devices_to_track = { device: { 'name': temp_devices_to_track[device], - 'state': STATE_DEVICE_DEFAULT, '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 } self.all_devices_state = STATE_DEVICE_DEFAULT @@ -40,30 +38,21 @@ class DeviceTracker: statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT) 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())) - def device_state_categories(self): 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): - now = datetime.now() - 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.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) + self.statemachine.set_state(self.devices_to_track[device]['category'], state) def update_devices(self, found_devices): @@ -89,7 +78,7 @@ class DeviceTracker: # 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 diff --git a/app/HomeAssistant.py b/app/HomeAssistant.py index f34a245d114..5a9f3ad0ed2 100644 --- a/app/HomeAssistant.py +++ b/app/HomeAssistant.py @@ -4,6 +4,7 @@ import time from app.StateMachine import StateMachine from app.EventBus import EventBus from app.DeviceTracker import DeviceTracker +from HttpInterface import HttpInterface from app.observer.WeatherWatcher import WeatherWatcher from app.observer.TomatoDeviceScanner import TomatoDeviceScanner @@ -23,7 +24,7 @@ class HomeAssistant: self.devicetracker = None self.huetrigger = None - + self.httpinterface = None def get_config(self): if self.config is None: @@ -76,6 +77,12 @@ class HomeAssistant: 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): self.setup_timer().start() @@ -87,6 +94,10 @@ class HomeAssistant: print "" print "Interrupt received. Wrapping up and quiting.." self.timer.stop() + + if self.httpinterface is not None: + self.httpinterface.stop() + break diff --git a/app/HttpInterface.py b/app/HttpInterface.py new file mode 100644 index 00000000000..36bc34f269e --- /dev/null +++ b/app/HttpInterface.py @@ -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("") + write("") + + for category, state, last_changed in self.server.statemachine.get_states(): + categories.append(category) + + write("".format(category, state, last_changed.strftime("%H:%M:%S %d-%m-%Y"))) + + write("
NameStateLast Changed
{}{}{}
") + + # Small form to change the state + write("
Change state:
") + write("
") + write("") + + write("") + write("") + write("
") + + 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)) \ No newline at end of file diff --git a/app/StateMachine.py b/app/StateMachine.py index e3e6efb41aa..8b6347e2bb7 100644 --- a/app/StateMachine.py +++ b/app/StateMachine.py @@ -38,6 +38,10 @@ class StateMachine: 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): fromState = ensure_list(fromState) diff --git a/app/actor/HueTrigger.py b/app/actor/HueTrigger.py index f69abea86b2..94ac5b42066 100644 --- a/app/actor/HueTrigger.py +++ b/app/actor/HueTrigger.py @@ -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.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: 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) # 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: - self.handle_sun_rising() + if statemachine.get_state(STATE_CATEGORY_SUN) == SUN_STATE_ABOVE_HORIZON: + self.handle_sun_rising(None, None, None) + def get_lights_status(self): 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) - def handle_sun_rising(self, event=None): + def handle_sun_rising(self, category, oldState, newState): # 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) diff --git a/start.py b/start.py index 5b0fefbd2a4..b27c4fc5eb8 100644 --- a/start.py +++ b/start.py @@ -6,5 +6,6 @@ ha = HomeAssistant() ha.setup_device_tracker(TomatoDeviceScanner(ha.get_config())) ha.setup_hue_trigger() +ha.setup_http_interface() ha.start()