mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 03:37:07 +00:00
Migrate to cherrypy wsgi from eventlet (#2387)
This commit is contained in:
parent
7582eb9f63
commit
d1f4901d53
@ -6,7 +6,7 @@ https://home-assistant.io/developers/api/
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from time import time
|
import queue
|
||||||
|
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
import homeassistant.remote as rem
|
import homeassistant.remote as rem
|
||||||
@ -72,19 +72,14 @@ 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.queue import LightQueue, Empty
|
|
||||||
import eventlet
|
|
||||||
|
|
||||||
cur_hub = eventlet.hubs.get_hub()
|
|
||||||
request.environ['eventlet.minimum_write_chunk_size'] = 0
|
|
||||||
to_write = LightQueue()
|
|
||||||
stop_obj = object()
|
stop_obj = object()
|
||||||
|
to_write = queue.Queue()
|
||||||
|
|
||||||
restrict = request.args.get('restrict')
|
restrict = request.args.get('restrict')
|
||||||
if restrict:
|
if restrict:
|
||||||
restrict = restrict.split(',')
|
restrict = restrict.split(',') + [EVENT_HOMEASSISTANT_STOP]
|
||||||
|
|
||||||
def thread_forward_events(event):
|
def forward_events(event):
|
||||||
"""Forward events to the open request."""
|
"""Forward events to the open request."""
|
||||||
if event.event_type == EVENT_TIME_CHANGED:
|
if event.event_type == EVENT_TIME_CHANGED:
|
||||||
return
|
return
|
||||||
@ -99,28 +94,20 @@ class APIEventStream(HomeAssistantView):
|
|||||||
else:
|
else:
|
||||||
data = json.dumps(event, cls=rem.JSONEncoder)
|
data = json.dumps(event, cls=rem.JSONEncoder)
|
||||||
|
|
||||||
cur_hub.schedule_call_global(0, lambda: to_write.put(data))
|
to_write.put(data)
|
||||||
|
|
||||||
def stream():
|
def stream():
|
||||||
"""Stream events to response."""
|
"""Stream events to response."""
|
||||||
self.hass.bus.listen(MATCH_ALL, thread_forward_events)
|
self.hass.bus.listen(MATCH_ALL, forward_events)
|
||||||
|
|
||||||
_LOGGER.debug('STREAM %s ATTACHED', id(stop_obj))
|
_LOGGER.debug('STREAM %s ATTACHED', id(stop_obj))
|
||||||
|
|
||||||
last_msg = time()
|
|
||||||
# Fire off one message right away to have browsers fire open event
|
# Fire off one message right away to have browsers fire open event
|
||||||
to_write.put(STREAM_PING_PAYLOAD)
|
to_write.put(STREAM_PING_PAYLOAD)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
# Somehow our queue.get sometimes takes too long to
|
payload = to_write.get(timeout=STREAM_PING_INTERVAL)
|
||||||
# be notified of arrival of data. Probably
|
|
||||||
# because of our spawning on hub in other thread
|
|
||||||
# hack. Because current goal is to get this out,
|
|
||||||
# We just timeout every second because it will
|
|
||||||
# return right away if qsize() > 0.
|
|
||||||
# So yes, we're basically polling :(
|
|
||||||
payload = to_write.get(timeout=1)
|
|
||||||
|
|
||||||
if payload is stop_obj:
|
if payload is stop_obj:
|
||||||
break
|
break
|
||||||
@ -129,15 +116,13 @@ class APIEventStream(HomeAssistantView):
|
|||||||
_LOGGER.debug('STREAM %s WRITING %s', id(stop_obj),
|
_LOGGER.debug('STREAM %s WRITING %s', id(stop_obj),
|
||||||
msg.strip())
|
msg.strip())
|
||||||
yield msg.encode("UTF-8")
|
yield msg.encode("UTF-8")
|
||||||
last_msg = time()
|
except queue.Empty:
|
||||||
except Empty:
|
|
||||||
if time() - last_msg > 50:
|
|
||||||
to_write.put(STREAM_PING_PAYLOAD)
|
to_write.put(STREAM_PING_PAYLOAD)
|
||||||
except GeneratorExit:
|
except GeneratorExit:
|
||||||
_LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj))
|
|
||||||
break
|
break
|
||||||
|
|
||||||
self.hass.bus.remove_listener(MATCH_ALL, thread_forward_events)
|
_LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj))
|
||||||
|
self.hass.bus.remove_listener(MATCH_ALL, forward_events)
|
||||||
|
|
||||||
return self.Response(stream(), mimetype='text/event-stream')
|
return self.Response(stream(), mimetype='text/event-stream')
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ For more details about this component, please refer to the documentation at
|
|||||||
https://home-assistant.io/components/camera/
|
https://home-assistant.io/components/camera/
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
@ -81,8 +82,6 @@ class Camera(Entity):
|
|||||||
|
|
||||||
def mjpeg_stream(self, response):
|
def mjpeg_stream(self, response):
|
||||||
"""Generate an HTTP MJPEG stream from camera images."""
|
"""Generate an HTTP MJPEG stream from camera images."""
|
||||||
import eventlet
|
|
||||||
|
|
||||||
def stream():
|
def stream():
|
||||||
"""Stream images as mjpeg stream."""
|
"""Stream images as mjpeg stream."""
|
||||||
try:
|
try:
|
||||||
@ -99,7 +98,7 @@ class Camera(Entity):
|
|||||||
|
|
||||||
last_image = img_bytes
|
last_image = img_bytes
|
||||||
|
|
||||||
eventlet.sleep(0.5)
|
time.sleep(0.5)
|
||||||
except GeneratorExit:
|
except GeneratorExit:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -13,19 +13,19 @@ import re
|
|||||||
import ssl
|
import ssl
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
import homeassistant.core as ha
|
|
||||||
import homeassistant.remote as rem
|
import homeassistant.remote as rem
|
||||||
from homeassistant import util
|
from homeassistant import util
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
SERVER_PORT, HTTP_HEADER_HA_AUTH, HTTP_HEADER_CACHE_CONTROL,
|
SERVER_PORT, HTTP_HEADER_HA_AUTH, HTTP_HEADER_CACHE_CONTROL,
|
||||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||||
HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, ALLOWED_CORS_HEADERS)
|
HTTP_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, ALLOWED_CORS_HEADERS,
|
||||||
|
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START)
|
||||||
from homeassistant.helpers.entity import split_entity_id
|
from homeassistant.helpers.entity import split_entity_id
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
DOMAIN = "http"
|
DOMAIN = "http"
|
||||||
REQUIREMENTS = ("eventlet==0.19.0", "static3==0.7.0", "Werkzeug==0.11.10")
|
REQUIREMENTS = ("cherrypy==6.0.2", "static3==0.7.0", "Werkzeug==0.11.10")
|
||||||
|
|
||||||
CONF_API_PASSWORD = "api_password"
|
CONF_API_PASSWORD = "api_password"
|
||||||
CONF_SERVER_HOST = "server_host"
|
CONF_SERVER_HOST = "server_host"
|
||||||
@ -118,11 +118,17 @@ def setup(hass, config):
|
|||||||
cors_origins=cors_origins
|
cors_origins=cors_origins
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.bus.listen_once(
|
def start_wsgi_server(event):
|
||||||
ha.EVENT_HOMEASSISTANT_START,
|
"""Start the WSGI server."""
|
||||||
lambda event:
|
server.start()
|
||||||
threading.Thread(target=server.start, daemon=True,
|
|
||||||
name='WSGI-server').start())
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_wsgi_server)
|
||||||
|
|
||||||
|
def stop_wsgi_server(event):
|
||||||
|
"""Stop the WSGI server."""
|
||||||
|
server.stop()
|
||||||
|
|
||||||
|
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_wsgi_server)
|
||||||
|
|
||||||
hass.wsgi = server
|
hass.wsgi = server
|
||||||
hass.config.api = rem.API(server_host if server_host != '0.0.0.0'
|
hass.config.api = rem.API(server_host if server_host != '0.0.0.0'
|
||||||
@ -241,6 +247,7 @@ class HomeAssistantWSGI(object):
|
|||||||
self.server_port = server_port
|
self.server_port = server_port
|
||||||
self.cors_origins = cors_origins
|
self.cors_origins = cors_origins
|
||||||
self.event_forwarder = None
|
self.event_forwarder = None
|
||||||
|
self.server = None
|
||||||
|
|
||||||
def register_view(self, view):
|
def register_view(self, view):
|
||||||
"""Register a view with the WSGI server.
|
"""Register a view with the WSGI server.
|
||||||
@ -308,17 +315,34 @@ class HomeAssistantWSGI(object):
|
|||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start the wsgi server."""
|
"""Start the wsgi server."""
|
||||||
from eventlet import wsgi
|
from cherrypy import wsgiserver
|
||||||
import eventlet
|
from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods,super-init-not-called
|
||||||
|
class ContextSSLAdapter(BuiltinSSLAdapter):
|
||||||
|
"""SSL Adapter that takes in an SSL context."""
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
self.server = wsgiserver.CherryPyWSGIServer(
|
||||||
|
(self.server_host, self.server_port), self,
|
||||||
|
server_name='Home Assistant')
|
||||||
|
|
||||||
sock = eventlet.listen((self.server_host, self.server_port))
|
|
||||||
if self.ssl_certificate:
|
if self.ssl_certificate:
|
||||||
context = ssl.SSLContext(SSL_VERSION)
|
context = ssl.SSLContext(SSL_VERSION)
|
||||||
context.options |= SSL_OPTS
|
context.options |= SSL_OPTS
|
||||||
context.set_ciphers(CIPHERS)
|
context.set_ciphers(CIPHERS)
|
||||||
context.load_cert_chain(self.ssl_certificate, self.ssl_key)
|
context.load_cert_chain(self.ssl_certificate, self.ssl_key)
|
||||||
sock = context.wrap_socket(sock, server_side=True)
|
self.server.ssl_adapter = ContextSSLAdapter(context)
|
||||||
wsgi.server(sock, self, log=_LOGGER)
|
|
||||||
|
threading.Thread(target=self.server.start, daemon=True,
|
||||||
|
name='WSGI-server').start()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Stop the wsgi server."""
|
||||||
|
self.server.stop()
|
||||||
|
|
||||||
def dispatch_request(self, request):
|
def dispatch_request(self, request):
|
||||||
"""Handle incoming request."""
|
"""Handle incoming request."""
|
||||||
@ -365,6 +389,10 @@ class HomeAssistantWSGI(object):
|
|||||||
"""Handle a request for base app + extra apps."""
|
"""Handle a request for base app + extra apps."""
|
||||||
from werkzeug.wsgi import DispatcherMiddleware
|
from werkzeug.wsgi import DispatcherMiddleware
|
||||||
|
|
||||||
|
if not self.hass.is_running:
|
||||||
|
from werkzeug.exceptions import BadRequest
|
||||||
|
return BadRequest()(environ, start_response)
|
||||||
|
|
||||||
app = DispatcherMiddleware(self.base_app, self.extra_apps)
|
app = DispatcherMiddleware(self.base_app, self.extra_apps)
|
||||||
# Strip out any cachebusting MD5 fingerprints
|
# Strip out any cachebusting MD5 fingerprints
|
||||||
fingerprinted = _FINGERPRINT.match(environ.get('PATH_INFO', ''))
|
fingerprinted = _FINGERPRINT.match(environ.get('PATH_INFO', ''))
|
||||||
|
@ -49,6 +49,19 @@ MIN_WORKER_THREAD = 2
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CoreState(enum.Enum):
|
||||||
|
"""Represent the current state of Home Assistant."""
|
||||||
|
|
||||||
|
not_running = "NOT_RUNNING"
|
||||||
|
starting = "STARTING"
|
||||||
|
running = "RUNNING"
|
||||||
|
stopping = "STOPPING"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return the event."""
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistant(object):
|
class HomeAssistant(object):
|
||||||
"""Root object of the Home Assistant home automation."""
|
"""Root object of the Home Assistant home automation."""
|
||||||
|
|
||||||
@ -59,14 +72,23 @@ class HomeAssistant(object):
|
|||||||
self.services = ServiceRegistry(self.bus, pool)
|
self.services = ServiceRegistry(self.bus, pool)
|
||||||
self.states = StateMachine(self.bus)
|
self.states = StateMachine(self.bus)
|
||||||
self.config = Config()
|
self.config = Config()
|
||||||
|
self.state = CoreState.not_running
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_running(self):
|
||||||
|
"""Return if Home Assistant is running."""
|
||||||
|
return self.state == CoreState.running
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Start home assistant."""
|
"""Start home assistant."""
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Starting Home Assistant (%d threads)", self.pool.worker_count)
|
"Starting Home Assistant (%d threads)", self.pool.worker_count)
|
||||||
|
self.state = CoreState.starting
|
||||||
|
|
||||||
create_timer(self)
|
create_timer(self)
|
||||||
self.bus.fire(EVENT_HOMEASSISTANT_START)
|
self.bus.fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
self.pool.block_till_done()
|
||||||
|
self.state = CoreState.running
|
||||||
|
|
||||||
def block_till_stopped(self):
|
def block_till_stopped(self):
|
||||||
"""Register service homeassistant/stop and will block until called."""
|
"""Register service homeassistant/stop and will block until called."""
|
||||||
@ -113,8 +135,10 @@ class HomeAssistant(object):
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop Home Assistant and shuts down all threads."""
|
"""Stop Home Assistant and shuts down all threads."""
|
||||||
_LOGGER.info("Stopping")
|
_LOGGER.info("Stopping")
|
||||||
|
self.state = CoreState.stopping
|
||||||
self.bus.fire(EVENT_HOMEASSISTANT_STOP)
|
self.bus.fire(EVENT_HOMEASSISTANT_STOP)
|
||||||
self.pool.stop()
|
self.pool.stop()
|
||||||
|
self.state = CoreState.not_running
|
||||||
|
|
||||||
|
|
||||||
class JobPriority(util.OrderedEnum):
|
class JobPriority(util.OrderedEnum):
|
||||||
|
@ -11,6 +11,7 @@ from datetime import datetime
|
|||||||
import enum
|
import enum
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
import threading
|
import threading
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
@ -123,6 +124,7 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
self.services = ha.ServiceRegistry(self.bus, pool)
|
self.services = ha.ServiceRegistry(self.bus, pool)
|
||||||
self.states = StateMachine(self.bus, self.remote_api)
|
self.states = StateMachine(self.bus, self.remote_api)
|
||||||
self.config = ha.Config()
|
self.config = ha.Config()
|
||||||
|
self.state = ha.CoreState.not_running
|
||||||
|
|
||||||
self.config.api = local_api
|
self.config.api = local_api
|
||||||
|
|
||||||
@ -134,17 +136,20 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
'Unable to setup local API to receive events')
|
'Unable to setup local API to receive events')
|
||||||
|
|
||||||
|
self.state = ha.CoreState.starting
|
||||||
ha.create_timer(self)
|
ha.create_timer(self)
|
||||||
|
|
||||||
self.bus.fire(ha.EVENT_HOMEASSISTANT_START,
|
self.bus.fire(ha.EVENT_HOMEASSISTANT_START,
|
||||||
origin=ha.EventOrigin.remote)
|
origin=ha.EventOrigin.remote)
|
||||||
|
|
||||||
# Give eventlet time to startup
|
# Ensure local HTTP is started
|
||||||
import eventlet
|
self.pool.block_till_done()
|
||||||
eventlet.sleep(0.1)
|
self.state = ha.CoreState.running
|
||||||
|
time.sleep(0.05)
|
||||||
|
|
||||||
# Setup that events from remote_api get forwarded to local_api
|
# Setup that events from remote_api get forwarded to local_api
|
||||||
# Do this after we fire START, otherwise HTTP is not started
|
# Do this after we are running, otherwise HTTP is not started
|
||||||
|
# or requests are blocked
|
||||||
if not connect_remote_events(self.remote_api, self.config.api):
|
if not connect_remote_events(self.remote_api, self.config.api):
|
||||||
raise HomeAssistantError((
|
raise HomeAssistantError((
|
||||||
'Could not setup event forwarding from api {} to '
|
'Could not setup event forwarding from api {} to '
|
||||||
@ -153,6 +158,7 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop Home Assistant and shuts down all threads."""
|
"""Stop Home Assistant and shuts down all threads."""
|
||||||
_LOGGER.info("Stopping")
|
_LOGGER.info("Stopping")
|
||||||
|
self.state = ha.CoreState.stopping
|
||||||
|
|
||||||
self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP,
|
self.bus.fire(ha.EVENT_HOMEASSISTANT_STOP,
|
||||||
origin=ha.EventOrigin.remote)
|
origin=ha.EventOrigin.remote)
|
||||||
@ -161,6 +167,7 @@ class HomeAssistant(ha.HomeAssistant):
|
|||||||
|
|
||||||
# Disconnect master event forwarding
|
# Disconnect master event forwarding
|
||||||
disconnect_remote_events(self.remote_api, self.config.api)
|
disconnect_remote_events(self.remote_api, self.config.api)
|
||||||
|
self.state = ha.CoreState.not_running
|
||||||
|
|
||||||
|
|
||||||
class EventBus(ha.EventBus):
|
class EventBus(ha.EventBus):
|
||||||
|
@ -5,7 +5,6 @@ pytz>=2016.4
|
|||||||
pip>=7.0.0
|
pip>=7.0.0
|
||||||
jinja2>=2.8
|
jinja2>=2.8
|
||||||
voluptuous==0.8.9
|
voluptuous==0.8.9
|
||||||
eventlet==0.19.0
|
|
||||||
|
|
||||||
# homeassistant.components.isy994
|
# homeassistant.components.isy994
|
||||||
PyISY==1.0.6
|
PyISY==1.0.6
|
||||||
@ -48,6 +47,9 @@ blockchain==1.3.3
|
|||||||
# homeassistant.components.notify.aws_sqs
|
# homeassistant.components.notify.aws_sqs
|
||||||
boto3==1.3.1
|
boto3==1.3.1
|
||||||
|
|
||||||
|
# homeassistant.components.http
|
||||||
|
cherrypy==6.0.2
|
||||||
|
|
||||||
# homeassistant.components.notify.xmpp
|
# homeassistant.components.notify.xmpp
|
||||||
dnspython3==1.12.0
|
dnspython3==1.12.0
|
||||||
|
|
||||||
@ -61,9 +63,6 @@ eliqonline==1.0.12
|
|||||||
# homeassistant.components.enocean
|
# homeassistant.components.enocean
|
||||||
enocean==0.31
|
enocean==0.31
|
||||||
|
|
||||||
# homeassistant.components.http
|
|
||||||
eventlet==0.19.0
|
|
||||||
|
|
||||||
# homeassistant.components.thermostat.honeywell
|
# homeassistant.components.thermostat.honeywell
|
||||||
evohomeclient==0.2.5
|
evohomeclient==0.2.5
|
||||||
|
|
||||||
|
1
setup.py
1
setup.py
@ -17,7 +17,6 @@ REQUIRES = [
|
|||||||
'pip>=7.0.0',
|
'pip>=7.0.0',
|
||||||
'jinja2>=2.8',
|
'jinja2>=2.8',
|
||||||
'voluptuous==0.8.9',
|
'voluptuous==0.8.9',
|
||||||
'eventlet==0.19.0',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"""The tests the for Locative device tracker platform."""
|
"""The tests the for Locative device tracker platform."""
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant import bootstrap, const
|
from homeassistant import bootstrap, const
|
||||||
@ -32,12 +32,9 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
bootstrap.setup_component(hass, http.DOMAIN, {
|
bootstrap.setup_component(hass, http.DOMAIN, {
|
||||||
http.DOMAIN: {
|
http.DOMAIN: {
|
||||||
http.CONF_SERVER_PORT: SERVER_PORT
|
http.CONF_SERVER_PORT: SERVER_PORT
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
# Set up API
|
|
||||||
bootstrap.setup_component(hass, 'api')
|
|
||||||
|
|
||||||
# Set up device tracker
|
# Set up device tracker
|
||||||
bootstrap.setup_component(hass, device_tracker.DOMAIN, {
|
bootstrap.setup_component(hass, device_tracker.DOMAIN, {
|
||||||
device_tracker.DOMAIN: {
|
device_tracker.DOMAIN: {
|
||||||
@ -46,7 +43,7 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
})
|
})
|
||||||
|
|
||||||
hass.start()
|
hass.start()
|
||||||
eventlet.sleep(0.05)
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule(): # pylint: disable=invalid-name
|
def tearDownModule(): # pylint: disable=invalid-name
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
"""The tests for the Alexa component."""
|
"""The tests for the Alexa component."""
|
||||||
# pylint: disable=protected-access,too-many-public-methods
|
# pylint: disable=protected-access,too-many-public-methods
|
||||||
import unittest
|
|
||||||
import json
|
import json
|
||||||
|
import time
|
||||||
|
import unittest
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant import bootstrap, const
|
from homeassistant import bootstrap, const
|
||||||
@ -86,8 +86,7 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
})
|
})
|
||||||
|
|
||||||
hass.start()
|
hass.start()
|
||||||
|
time.sleep(0.05)
|
||||||
eventlet.sleep(0.1)
|
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule(): # pylint: disable=invalid-name
|
def tearDownModule(): # pylint: disable=invalid-name
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
"""The tests for the Home Assistant API component."""
|
"""The tests for the Home Assistant API 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 time
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant import bootstrap, const
|
from homeassistant import bootstrap, const
|
||||||
@ -48,10 +48,7 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
bootstrap.setup_component(hass, 'api')
|
bootstrap.setup_component(hass, 'api')
|
||||||
|
|
||||||
hass.start()
|
hass.start()
|
||||||
|
time.sleep(0.05)
|
||||||
# To start HTTP
|
|
||||||
# TODO fix this
|
|
||||||
eventlet.sleep(0.05)
|
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule(): # pylint: disable=invalid-name
|
def tearDownModule(): # pylint: disable=invalid-name
|
||||||
@ -387,25 +384,23 @@ class TestAPI(unittest.TestCase):
|
|||||||
headers=HA_HEADERS)
|
headers=HA_HEADERS)
|
||||||
self.assertEqual(422, req.status_code)
|
self.assertEqual(422, req.status_code)
|
||||||
|
|
||||||
# TODO disabled because eventlet cannot validate
|
# Setup a real one
|
||||||
# a connection to itself, need a second instance
|
req = requests.post(
|
||||||
# # Setup a real one
|
_url(const.URL_API_EVENT_FORWARD),
|
||||||
# req = requests.post(
|
data=json.dumps({
|
||||||
# _url(const.URL_API_EVENT_FORWARD),
|
'api_password': API_PASSWORD,
|
||||||
# data=json.dumps({
|
'host': '127.0.0.1',
|
||||||
# 'api_password': API_PASSWORD,
|
'port': SERVER_PORT
|
||||||
# 'host': '127.0.0.1',
|
}),
|
||||||
# 'port': SERVER_PORT
|
headers=HA_HEADERS)
|
||||||
# }),
|
self.assertEqual(200, req.status_code)
|
||||||
# headers=HA_HEADERS)
|
|
||||||
# self.assertEqual(200, req.status_code)
|
|
||||||
|
|
||||||
# # Delete it again..
|
# Delete it again..
|
||||||
# req = requests.delete(
|
req = requests.delete(
|
||||||
# _url(const.URL_API_EVENT_FORWARD),
|
_url(const.URL_API_EVENT_FORWARD),
|
||||||
# data=json.dumps({}),
|
data=json.dumps({}),
|
||||||
# headers=HA_HEADERS)
|
headers=HA_HEADERS)
|
||||||
# self.assertEqual(400, req.status_code)
|
self.assertEqual(400, req.status_code)
|
||||||
|
|
||||||
req = requests.delete(
|
req = requests.delete(
|
||||||
_url(const.URL_API_EVENT_FORWARD),
|
_url(const.URL_API_EVENT_FORWARD),
|
||||||
@ -425,57 +420,58 @@ class TestAPI(unittest.TestCase):
|
|||||||
headers=HA_HEADERS)
|
headers=HA_HEADERS)
|
||||||
self.assertEqual(200, req.status_code)
|
self.assertEqual(200, req.status_code)
|
||||||
|
|
||||||
# def test_stream(self):
|
def test_stream(self):
|
||||||
# """Test the stream."""
|
"""Test the stream."""
|
||||||
# listen_count = self._listen_count()
|
listen_count = self._listen_count()
|
||||||
# with closing(requests.get(_url(const.URL_API_STREAM), timeout=3,
|
with closing(requests.get(_url(const.URL_API_STREAM), timeout=3,
|
||||||
# stream=True, headers=HA_HEADERS)) as req:
|
stream=True, headers=HA_HEADERS)) as req:
|
||||||
|
stream = req.iter_content(1)
|
||||||
|
self.assertEqual(listen_count + 1, self._listen_count())
|
||||||
|
|
||||||
# self.assertEqual(listen_count + 1, self._listen_count())
|
hass.bus.fire('test_event')
|
||||||
|
|
||||||
# hass.bus.fire('test_event')
|
data = self._stream_next_event(stream)
|
||||||
|
|
||||||
# data = self._stream_next_event(req)
|
self.assertEqual('test_event', data['event_type'])
|
||||||
|
|
||||||
# self.assertEqual('test_event', data['event_type'])
|
def test_stream_with_restricted(self):
|
||||||
|
"""Test the stream with restrictions."""
|
||||||
|
listen_count = self._listen_count()
|
||||||
|
url = _url('{}?restrict=test_event1,test_event3'.format(
|
||||||
|
const.URL_API_STREAM))
|
||||||
|
with closing(requests.get(url, stream=True, timeout=3,
|
||||||
|
headers=HA_HEADERS)) as req:
|
||||||
|
stream = req.iter_content(1)
|
||||||
|
self.assertEqual(listen_count + 1, self._listen_count())
|
||||||
|
|
||||||
# def test_stream_with_restricted(self):
|
hass.bus.fire('test_event1')
|
||||||
# """Test the stream with restrictions."""
|
data = self._stream_next_event(stream)
|
||||||
# listen_count = self._listen_count()
|
self.assertEqual('test_event1', data['event_type'])
|
||||||
# url = _url('{}?restrict=test_event1,test_event3'.format(
|
|
||||||
# const.URL_API_STREAM))
|
|
||||||
# with closing(requests.get(url, stream=True, timeout=3,
|
|
||||||
# headers=HA_HEADERS)) as req:
|
|
||||||
# self.assertEqual(listen_count + 1, self._listen_count())
|
|
||||||
|
|
||||||
# hass.bus.fire('test_event1')
|
hass.bus.fire('test_event2')
|
||||||
# data = self._stream_next_event(req)
|
hass.bus.fire('test_event3')
|
||||||
# self.assertEqual('test_event1', data['event_type'])
|
|
||||||
|
|
||||||
# hass.bus.fire('test_event2')
|
data = self._stream_next_event(stream)
|
||||||
# hass.bus.fire('test_event3')
|
self.assertEqual('test_event3', data['event_type'])
|
||||||
|
|
||||||
# data = self._stream_next_event(req)
|
def _stream_next_event(self, stream):
|
||||||
# self.assertEqual('test_event3', data['event_type'])
|
"""Read the stream for next event while ignoring ping."""
|
||||||
|
while True:
|
||||||
|
data = b''
|
||||||
|
last_new_line = False
|
||||||
|
for dat in stream:
|
||||||
|
if dat == b'\n' and last_new_line:
|
||||||
|
break
|
||||||
|
data += dat
|
||||||
|
last_new_line = dat == b'\n'
|
||||||
|
|
||||||
# def _stream_next_event(self, stream):
|
conv = data.decode('utf-8').strip()[6:]
|
||||||
# """Read the stream for next event while ignoring ping."""
|
|
||||||
# while True:
|
|
||||||
# data = b''
|
|
||||||
# last_new_line = False
|
|
||||||
# for dat in stream.iter_content(1):
|
|
||||||
# if dat == b'\n' and last_new_line:
|
|
||||||
# break
|
|
||||||
# data += dat
|
|
||||||
# last_new_line = dat == b'\n'
|
|
||||||
|
|
||||||
# conv = data.decode('utf-8').strip()[6:]
|
if conv != 'ping':
|
||||||
|
break
|
||||||
|
|
||||||
# if conv != 'ping':
|
return json.loads(conv)
|
||||||
# break
|
|
||||||
|
|
||||||
# return json.loads(conv)
|
def _listen_count(self):
|
||||||
|
"""Return number of event listeners."""
|
||||||
# def _listen_count(self):
|
return sum(hass.bus.listeners.values())
|
||||||
# """Return number of event listeners."""
|
|
||||||
# return sum(hass.bus.listeners.values())
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
"""The tests for Home Assistant frontend."""
|
"""The tests for Home Assistant frontend."""
|
||||||
# pylint: disable=protected-access,too-many-public-methods
|
# pylint: disable=protected-access,too-many-public-methods
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
import homeassistant.bootstrap as bootstrap
|
import homeassistant.bootstrap as bootstrap
|
||||||
@ -42,10 +42,7 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
bootstrap.setup_component(hass, 'frontend')
|
bootstrap.setup_component(hass, 'frontend')
|
||||||
|
|
||||||
hass.start()
|
hass.start()
|
||||||
|
time.sleep(0.05)
|
||||||
# Give eventlet time to start
|
|
||||||
# TODO fix this
|
|
||||||
eventlet.sleep(0.05)
|
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule(): # pylint: disable=invalid-name
|
def tearDownModule(): # pylint: disable=invalid-name
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"""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
|
||||||
import logging
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
import eventlet
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from homeassistant import bootstrap, const
|
from homeassistant import bootstrap, const
|
||||||
@ -43,8 +43,7 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
bootstrap.setup_component(hass, 'api')
|
bootstrap.setup_component(hass, 'api')
|
||||||
|
|
||||||
hass.start()
|
hass.start()
|
||||||
|
time.sleep(0.05)
|
||||||
eventlet.sleep(0.05)
|
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule(): # pylint: disable=invalid-name
|
def tearDownModule(): # pylint: disable=invalid-name
|
||||||
@ -83,7 +82,7 @@ class TestHttp:
|
|||||||
|
|
||||||
logs = caplog.text()
|
logs = caplog.text()
|
||||||
|
|
||||||
assert const.URL_API in logs
|
# assert const.URL_API in logs
|
||||||
assert API_PASSWORD not in logs
|
assert API_PASSWORD not in logs
|
||||||
|
|
||||||
def test_access_denied_with_wrong_password_in_url(self):
|
def test_access_denied_with_wrong_password_in_url(self):
|
||||||
@ -106,5 +105,5 @@ class TestHttp:
|
|||||||
|
|
||||||
logs = caplog.text()
|
logs = caplog.text()
|
||||||
|
|
||||||
assert const.URL_API in logs
|
# assert const.URL_API in logs
|
||||||
assert API_PASSWORD not in logs
|
assert API_PASSWORD not in logs
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
"""Test Home Assistant remote methods and classes."""
|
"""Test Home Assistant remote methods and classes."""
|
||||||
# pylint: disable=protected-access,too-many-public-methods
|
# pylint: disable=protected-access,too-many-public-methods
|
||||||
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import eventlet
|
|
||||||
|
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
import homeassistant.bootstrap as bootstrap
|
import homeassistant.bootstrap as bootstrap
|
||||||
import homeassistant.remote as remote
|
import homeassistant.remote as remote
|
||||||
@ -47,10 +46,7 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
bootstrap.setup_component(hass, 'api')
|
bootstrap.setup_component(hass, 'api')
|
||||||
|
|
||||||
hass.start()
|
hass.start()
|
||||||
|
time.sleep(0.05)
|
||||||
# Give eventlet time to start
|
|
||||||
# TODO fix this
|
|
||||||
eventlet.sleep(0.05)
|
|
||||||
|
|
||||||
master_api = remote.API("127.0.0.1", API_PASSWORD, MASTER_PORT)
|
master_api = remote.API("127.0.0.1", API_PASSWORD, MASTER_PORT)
|
||||||
|
|
||||||
@ -63,10 +59,6 @@ def setUpModule(): # pylint: disable=invalid-name
|
|||||||
|
|
||||||
slave.start()
|
slave.start()
|
||||||
|
|
||||||
# Give eventlet time to start
|
|
||||||
# TODO fix this
|
|
||||||
eventlet.sleep(0.05)
|
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule(): # pylint: disable=invalid-name
|
def tearDownModule(): # pylint: disable=invalid-name
|
||||||
"""Stop the Home Assistant server and slave."""
|
"""Stop the Home Assistant server and slave."""
|
||||||
@ -257,7 +249,6 @@ class TestRemoteClasses(unittest.TestCase):
|
|||||||
slave.pool.block_till_done()
|
slave.pool.block_till_done()
|
||||||
# Wait till master gives updated state
|
# Wait till master gives updated state
|
||||||
hass.pool.block_till_done()
|
hass.pool.block_till_done()
|
||||||
eventlet.sleep(0.01)
|
|
||||||
|
|
||||||
self.assertEqual("remote.statemachine test",
|
self.assertEqual("remote.statemachine test",
|
||||||
slave.states.get("remote.test").state)
|
slave.states.get("remote.test").state)
|
||||||
@ -266,13 +257,11 @@ class TestRemoteClasses(unittest.TestCase):
|
|||||||
"""Remove statemachine from master."""
|
"""Remove statemachine from master."""
|
||||||
hass.states.set("remote.master_remove", "remove me!")
|
hass.states.set("remote.master_remove", "remove me!")
|
||||||
hass.pool.block_till_done()
|
hass.pool.block_till_done()
|
||||||
eventlet.sleep(0.01)
|
|
||||||
|
|
||||||
self.assertIn('remote.master_remove', slave.states.entity_ids())
|
self.assertIn('remote.master_remove', slave.states.entity_ids())
|
||||||
|
|
||||||
hass.states.remove("remote.master_remove")
|
hass.states.remove("remote.master_remove")
|
||||||
hass.pool.block_till_done()
|
hass.pool.block_till_done()
|
||||||
eventlet.sleep(0.01)
|
|
||||||
|
|
||||||
self.assertNotIn('remote.master_remove', slave.states.entity_ids())
|
self.assertNotIn('remote.master_remove', slave.states.entity_ids())
|
||||||
|
|
||||||
@ -280,14 +269,12 @@ class TestRemoteClasses(unittest.TestCase):
|
|||||||
"""Remove statemachine from slave."""
|
"""Remove statemachine from slave."""
|
||||||
hass.states.set("remote.slave_remove", "remove me!")
|
hass.states.set("remote.slave_remove", "remove me!")
|
||||||
hass.pool.block_till_done()
|
hass.pool.block_till_done()
|
||||||
eventlet.sleep(0.01)
|
|
||||||
|
|
||||||
self.assertIn('remote.slave_remove', slave.states.entity_ids())
|
self.assertIn('remote.slave_remove', slave.states.entity_ids())
|
||||||
|
|
||||||
self.assertTrue(slave.states.remove("remote.slave_remove"))
|
self.assertTrue(slave.states.remove("remote.slave_remove"))
|
||||||
slave.pool.block_till_done()
|
slave.pool.block_till_done()
|
||||||
hass.pool.block_till_done()
|
hass.pool.block_till_done()
|
||||||
eventlet.sleep(0.01)
|
|
||||||
|
|
||||||
self.assertNotIn('remote.slave_remove', slave.states.entity_ids())
|
self.assertNotIn('remote.slave_remove', slave.states.entity_ids())
|
||||||
|
|
||||||
@ -306,6 +293,5 @@ class TestRemoteClasses(unittest.TestCase):
|
|||||||
slave.pool.block_till_done()
|
slave.pool.block_till_done()
|
||||||
# Wait till master gives updated event
|
# Wait till master gives updated event
|
||||||
hass.pool.block_till_done()
|
hass.pool.block_till_done()
|
||||||
eventlet.sleep(0.01)
|
|
||||||
|
|
||||||
self.assertEqual(1, len(test_value))
|
self.assertEqual(1, len(test_value))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user