This commit is contained in:
Paulus Schoutsen 2016-05-14 15:07:20 -07:00
parent 54ecab7590
commit 1096232e17
4 changed files with 88 additions and 39 deletions

View file

@ -73,51 +73,70 @@ class APIEventStream(HomeAssistantView):
def get(self, request): def get(self, request):
"""Provide a streaming interface for the event bus.""" """Provide a streaming interface for the event bus."""
from eventlet import Queue import eventlet
from eventlet import queue as eventlet_queue
import queue as thread_queue
from threading import Event
from time import time
queue = Queue() to_write = thread_queue.Queue()
# to_write = eventlet.Queue()
stop_obj = object() stop_obj = object()
hass = self.hass hass = self.hass
connection_closed = Event()
restrict = request.args.get('restrict') restrict = request.args.get('restrict')
if restrict: if restrict:
restrict = restrict.split(',') restrict = restrict.split(',')
restrict = False
def ping(now): def ping(now):
"""Add a ping message to queue.""" """Add a ping message to queue."""
queue.put(STREAM_PING_PAYLOAD) print(id(stop_obj), 'ping')
to_write.put(STREAM_PING_PAYLOAD)
def forward_events(event): def forward_events(event):
"""Forward events to the open request.""" """Forward events to the open request."""
print(id(stop_obj), 'forwarding', event)
if event.event_type == EVENT_TIME_CHANGED: if event.event_type == EVENT_TIME_CHANGED:
pass pass
elif event.event_type == EVENT_HOMEASSISTANT_STOP: elif event.event_type == EVENT_HOMEASSISTANT_STOP:
queue.put(stop_obj) to_write.put(stop_obj)
else: else:
queue.put(json.dumps(event, cls=rem.JSONEncoder)) to_write.put(json.dumps(event, cls=rem.JSONEncoder))
def stream(): def stream():
"""Stream events to response.""" """Stream events to response."""
if restrict: if restrict:
for event in restrict: for event_type in restrict:
hass.bus.listen(event, forward_events) hass.bus.listen(event_type, forward_events)
else: else:
hass.bus.listen(MATCH_ALL, forward_events) hass.bus.listen(MATCH_ALL, forward_events)
attached_ping = track_utc_time_change(hass, ping, second=(0, 30)) attached_ping = track_utc_time_change(
hass, ping, second=(0, 30))
try: print(id(stop_obj), 'attached goodness')
while True:
payload = queue.get() while not connection_closed.is_set():
try:
print(id(stop_obj), "Try getting obj")
payload = to_write.get(False)
if payload is stop_obj: if payload is stop_obj:
break break
msg = "data: {}\n\n".format(payload) msg = "data: {}\n\n".format(payload)
print(id(stop_obj), msg)
yield msg.encode("UTF-8") yield msg.encode("UTF-8")
except GeneratorExit: except eventlet_queue.Empty:
pass print(id(stop_obj), "queue empty, sleep 0.5")
eventlet.sleep(.5)
except GeneratorExit:
pass
print(id(stop_obj), "cleaning up")
hass.bus.remove_listener(EVENT_TIME_CHANGED, attached_ping) hass.bus.remove_listener(EVENT_TIME_CHANGED, attached_ping)
@ -127,7 +146,29 @@ class APIEventStream(HomeAssistantView):
else: else:
hass.bus.remove_listener(MATCH_ALL, forward_events) hass.bus.remove_listener(MATCH_ALL, forward_events)
return self.Response(stream(), mimetype='text/event-stream') resp = self.Response(stream(), mimetype='text/event-stream')
def closing():
print()
print()
print()
print()
print()
print()
print()
print()
print(id(stop_obj), "CLOSING RESPONSE")
print()
print()
print()
print()
print()
print()
print()
connection_closed.set()
resp.call_on_close(closing)
return resp
class APIConfigView(HomeAssistantView): class APIConfigView(HomeAssistantView):

View file

@ -70,7 +70,8 @@ class IndexView(HomeAssistantView):
name = "frontend:index" name = "frontend:index"
requires_auth = False requires_auth = False
extra_urls = ['/logbook', '/history', '/map', '/devService', '/devState', extra_urls = ['/logbook', '/history', '/map', '/devService', '/devState',
'/devEvent', '/devInfo', '/devTemplate', '/states/<entity>'] '/devEvent', '/devInfo', '/devTemplate',
'/states', '/states/<entity_id>']
def __init__(self, hass): def __init__(self, hass):
"""Initialize the frontend view.""" """Initialize the frontend view."""
@ -84,13 +85,18 @@ class IndexView(HomeAssistantView):
) )
) )
def get(self, request): def get(self, request, entity_id=None):
"""Serve the index view.""" """Serve the index view."""
app_url = "frontend-{}.html".format(version.VERSION) if self.hass.wsgi.development:
app_url = 'home-assistant-polymer/src/home-assistant.html'
else:
app_url = "frontend-{}.html".format(version.VERSION)
# auto login if no password was set, else check api_password param # auto login if no password was set, else check api_password param
auth = ('no_password_set' if self.hass.config.api.api_password is None if self.hass.config.api.api_password is None:
else request.values.get('api_password', '')) auth = 'no_password_set'
else:
request.values.get('api_password', '')
template = self.templates.get_template('index.html') template = self.templates.get_template('index.html')

View file

@ -87,12 +87,11 @@ class HomeAssistantWSGI(object):
"""Initilalize the WSGI Home Assistant server.""" """Initilalize the WSGI Home Assistant server."""
from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequest
from werkzeug.wrappers import BaseRequest, AcceptMixin from werkzeug.wrappers import BaseRequest, AcceptMixin
from werkzeug.contrib.wrappers import JSONRequestMixin
from werkzeug.routing import Map from werkzeug.routing import Map
from werkzeug.utils import cached_property from werkzeug.utils import cached_property
from werkzeug.wrappers import Response from werkzeug.wrappers import Response
class Request(BaseRequest, AcceptMixin, JSONRequestMixin): class Request(BaseRequest, AcceptMixin):
"""Base class for incoming requests.""" """Base class for incoming requests."""
@cached_property @cached_property
@ -100,8 +99,8 @@ class HomeAssistantWSGI(object):
"""Get the result of json.loads if possible.""" """Get the result of json.loads if possible."""
if not self.data: if not self.data:
return None return None
elif 'json' not in self.environ.get('CONTENT_TYPE', ''): # elif 'json' not in self.environ.get('CONTENT_TYPE', ''):
raise BadRequest('Not a JSON request') # raise BadRequest('Not a JSON request')
try: try:
return json.loads(self.data.decode( return json.loads(self.data.decode(
self.charset, self.encoding_errors)) self.charset, self.encoding_errors))

View file

@ -1,6 +1,6 @@
"""The tests for the Home Assistant HTTP component.""" """The tests for the Home Assistant HTTP component."""
# pylint: disable=protected-access,too-many-public-methods # pylint: disable=protected-access,too-many-public-methods
# from contextlib import closing from contextlib import closing
import json import json
import tempfile import tempfile
import unittest import unittest
@ -443,6 +443,9 @@ class TestAPI(unittest.TestCase):
# self.assertEqual(listen_count + 1, self._listen_count()) # self.assertEqual(listen_count + 1, self._listen_count())
# # eventlet.sleep(1)
# print('firing event')
# hass.bus.fire('test_event') # hass.bus.fire('test_event')
# hass.pool.block_till_done() # hass.pool.block_till_done()
@ -476,20 +479,20 @@ class TestAPI(unittest.TestCase):
# data = self._stream_next_event(req) # data = self._stream_next_event(req)
# self.assertEqual('test_event3', data['event_type']) # self.assertEqual('test_event3', data['event_type'])
# def _stream_next_event(self, stream): def _stream_next_event(self, stream):
# """Test the stream for next event.""" """Test the stream for next event."""
# data = b'' data = b''
# last_new_line = False last_new_line = False
# for dat in stream.iter_content(1): for dat in stream.iter_content(1):
# if dat == b'\n' and last_new_line: if dat == b'\n' and last_new_line:
# break break
# data += dat data += dat
# last_new_line = dat == b'\n' last_new_line = dat == b'\n'
# conv = data.decode('utf-8').strip()[6:] conv = data.decode('utf-8').strip()[6:]
# return conv if conv == 'ping' else json.loads(conv) return conv if conv == 'ping' else json.loads(conv)
# def _listen_count(self): def _listen_count(self):
# """Return number of event listeners.""" """Return number of event listeners."""
# return sum(hass.bus.listeners.values()) return sum(hass.bus.listeners.values())