mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
commit
2c157e6885
@ -52,7 +52,7 @@ omit =
|
|||||||
homeassistant/components/discovery.py
|
homeassistant/components/discovery.py
|
||||||
homeassistant/components/downloader.py
|
homeassistant/components/downloader.py
|
||||||
homeassistant/components/ifttt.py
|
homeassistant/components/ifttt.py
|
||||||
homeassistant/components/influx.py
|
homeassistant/components/influxdb.py
|
||||||
homeassistant/components/keyboard.py
|
homeassistant/components/keyboard.py
|
||||||
homeassistant/components/light/hue.py
|
homeassistant/components/light/hue.py
|
||||||
homeassistant/components/light/mqtt.py
|
homeassistant/components/light/mqtt.py
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
""" DO NOT MODIFY. Auto-generated by build_frontend script """
|
||||||
VERSION = "8470cd10f28b20eae9022fa4c8f40c1b"
|
VERSION = "d07b7ed1734ae3f2472f9ae88e0c3dea"
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
Subproject commit feb776ec89d6872dad2203b352cc6d652c46356d
|
Subproject commit 044a6d9810b6aa662b82af0f67deb662725ad4cb
|
@ -6,16 +6,17 @@ This module provides an API and a HTTP interface for debug purposes.
|
|||||||
For more details about the RESTful API, please refer to the documentation at
|
For more details about the RESTful API, please refer to the documentation at
|
||||||
https://home-assistant.io/developers/api/
|
https://home-assistant.io/developers/api/
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
import threading
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
import gzip
|
|
||||||
import os
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from http.server import SimpleHTTPRequestHandler, HTTPServer
|
import gzip
|
||||||
from http import cookies
|
from http import cookies
|
||||||
|
from http.server import SimpleHTTPRequestHandler, HTTPServer
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
from socketserver import ThreadingMixIn
|
from socketserver import ThreadingMixIn
|
||||||
|
import ssl
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
@ -36,6 +37,8 @@ CONF_API_PASSWORD = "api_password"
|
|||||||
CONF_SERVER_HOST = "server_host"
|
CONF_SERVER_HOST = "server_host"
|
||||||
CONF_SERVER_PORT = "server_port"
|
CONF_SERVER_PORT = "server_port"
|
||||||
CONF_DEVELOPMENT = "development"
|
CONF_DEVELOPMENT = "development"
|
||||||
|
CONF_SSL_CERTIFICATE = 'ssl_certificate'
|
||||||
|
CONF_SSL_KEY = 'ssl_key'
|
||||||
|
|
||||||
DATA_API_PASSWORD = 'api_password'
|
DATA_API_PASSWORD = 'api_password'
|
||||||
|
|
||||||
@ -57,11 +60,13 @@ def setup(hass, config):
|
|||||||
server_host = conf.get(CONF_SERVER_HOST, '0.0.0.0')
|
server_host = conf.get(CONF_SERVER_HOST, '0.0.0.0')
|
||||||
server_port = conf.get(CONF_SERVER_PORT, SERVER_PORT)
|
server_port = conf.get(CONF_SERVER_PORT, SERVER_PORT)
|
||||||
development = str(conf.get(CONF_DEVELOPMENT, "")) == "1"
|
development = str(conf.get(CONF_DEVELOPMENT, "")) == "1"
|
||||||
|
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
|
||||||
|
ssl_key = conf.get(CONF_SSL_KEY)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server = HomeAssistantHTTPServer(
|
server = HomeAssistantHTTPServer(
|
||||||
(server_host, server_port), RequestHandler, hass, api_password,
|
(server_host, server_port), RequestHandler, hass, api_password,
|
||||||
development)
|
development, ssl_certificate, ssl_key)
|
||||||
except OSError:
|
except OSError:
|
||||||
# If address already in use
|
# If address already in use
|
||||||
_LOGGER.exception("Error setting up HTTP server")
|
_LOGGER.exception("Error setting up HTTP server")
|
||||||
@ -73,7 +78,8 @@ def setup(hass, config):
|
|||||||
threading.Thread(target=server.start, daemon=True).start())
|
threading.Thread(target=server.start, daemon=True).start())
|
||||||
|
|
||||||
hass.http = server
|
hass.http = server
|
||||||
hass.config.api = rem.API(util.get_local_ip(), api_password, server_port)
|
hass.config.api = rem.API(util.get_local_ip(), api_password, server_port,
|
||||||
|
ssl_certificate is not None)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -88,7 +94,7 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
def __init__(self, server_address, request_handler_class,
|
def __init__(self, server_address, request_handler_class,
|
||||||
hass, api_password, development):
|
hass, api_password, development, ssl_certificate, ssl_key):
|
||||||
super().__init__(server_address, request_handler_class)
|
super().__init__(server_address, request_handler_class)
|
||||||
|
|
||||||
self.server_address = server_address
|
self.server_address = server_address
|
||||||
@ -97,6 +103,7 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
self.development = development
|
self.development = development
|
||||||
self.paths = []
|
self.paths = []
|
||||||
self.sessions = SessionStore()
|
self.sessions = SessionStore()
|
||||||
|
self.use_ssl = ssl_certificate is not None
|
||||||
|
|
||||||
# We will lazy init this one if needed
|
# We will lazy init this one if needed
|
||||||
self.event_forwarder = None
|
self.event_forwarder = None
|
||||||
@ -104,6 +111,12 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
if development:
|
if development:
|
||||||
_LOGGER.info("running http in development mode")
|
_LOGGER.info("running http in development mode")
|
||||||
|
|
||||||
|
if ssl_certificate is not None:
|
||||||
|
wrap_kwargs = {'certfile': ssl_certificate}
|
||||||
|
if ssl_key is not None:
|
||||||
|
wrap_kwargs['keyfile'] = ssl_key
|
||||||
|
self.socket = ssl.wrap_socket(self.socket, **wrap_kwargs)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
""" Starts the HTTP server. """
|
""" Starts the HTTP server. """
|
||||||
def stop_http(event):
|
def stop_http(event):
|
||||||
@ -112,8 +125,11 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
|||||||
|
|
||||||
self.hass.bus.listen_once(ha.EVENT_HOMEASSISTANT_STOP, stop_http)
|
self.hass.bus.listen_once(ha.EVENT_HOMEASSISTANT_STOP, stop_http)
|
||||||
|
|
||||||
|
protocol = 'https' if self.use_ssl else 'http'
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Starting web interface at http://%s:%d", *self.server_address)
|
"Starting web interface at %s://%s:%d",
|
||||||
|
protocol, self.server_address[0], self.server_address[1])
|
||||||
|
|
||||||
# 31-1-2015: Refactored frontend/api components out of this component
|
# 31-1-2015: Refactored frontend/api components out of this component
|
||||||
# To prevent stuff from breaking, load the two extracted components
|
# To prevent stuff from breaking, load the two extracted components
|
||||||
@ -186,17 +202,12 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
|||||||
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
|
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.server.api_password is None:
|
self.authenticated = (self.server.api_password is None
|
||||||
self.authenticated = True
|
or self.headers.get(HTTP_HEADER_HA_AUTH) ==
|
||||||
elif HTTP_HEADER_HA_AUTH in self.headers:
|
self.server.api_password
|
||||||
api_password = self.headers.get(HTTP_HEADER_HA_AUTH)
|
or data.get(DATA_API_PASSWORD) ==
|
||||||
|
self.server.api_password
|
||||||
if not api_password and DATA_API_PASSWORD in data:
|
or self.verify_session())
|
||||||
api_password = data[DATA_API_PASSWORD]
|
|
||||||
|
|
||||||
self.authenticated = api_password == self.server.api_password
|
|
||||||
else:
|
|
||||||
self.authenticated = self.verify_session()
|
|
||||||
|
|
||||||
if '_METHOD' in data:
|
if '_METHOD' in data:
|
||||||
method = data.pop('_METHOD')
|
method = data.pop('_METHOD')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
""" Constants used by Home Assistant components. """
|
""" Constants used by Home Assistant components. """
|
||||||
|
|
||||||
__version__ = "0.9.0"
|
__version__ = "0.9.1"
|
||||||
|
|
||||||
# Can be used to specify a catch all when registering state or event listeners.
|
# Can be used to specify a catch all when registering state or event listeners.
|
||||||
MATCH_ALL = '*'
|
MATCH_ALL = '*'
|
||||||
|
@ -51,10 +51,13 @@ class API(object):
|
|||||||
""" Object to pass around Home Assistant API location and credentials. """
|
""" Object to pass around Home Assistant API location and credentials. """
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
def __init__(self, host, api_password=None, port=None):
|
def __init__(self, host, api_password=None, port=None, use_ssl=False):
|
||||||
self.host = host
|
self.host = host
|
||||||
self.port = port or SERVER_PORT
|
self.port = port or SERVER_PORT
|
||||||
self.api_password = api_password
|
self.api_password = api_password
|
||||||
|
if use_ssl:
|
||||||
|
self.base_url = "https://{}:{}".format(host, self.port)
|
||||||
|
else:
|
||||||
self.base_url = "http://{}:{}".format(host, self.port)
|
self.base_url = "http://{}:{}".format(host, self.port)
|
||||||
self.status = None
|
self.status = None
|
||||||
self._headers = {}
|
self._headers = {}
|
||||||
|
@ -26,7 +26,7 @@ https://github.com/nkgilley/python-ecobee-api/archive/92a2f330cbaf601d0618456fdd
|
|||||||
# homeassistant.components.ifttt
|
# homeassistant.components.ifttt
|
||||||
pyfttt==0.3
|
pyfttt==0.3
|
||||||
|
|
||||||
# homeassistant.components.influx
|
# homeassistant.components.influxdb
|
||||||
influxdb==2.10.0
|
influxdb==2.10.0
|
||||||
|
|
||||||
# homeassistant.components.isy994
|
# homeassistant.components.isy994
|
||||||
|
@ -66,18 +66,31 @@ class TestAPI(unittest.TestCase):
|
|||||||
|
|
||||||
# TODO move back to http component and test with use_auth.
|
# TODO move back to http component and test with use_auth.
|
||||||
def test_access_denied_without_password(self):
|
def test_access_denied_without_password(self):
|
||||||
req = requests.get(
|
req = requests.get(_url(const.URL_API))
|
||||||
_url(const.URL_API_STATES_ENTITY.format("test")))
|
|
||||||
|
|
||||||
self.assertEqual(401, req.status_code)
|
self.assertEqual(401, req.status_code)
|
||||||
|
|
||||||
def test_access_denied_with_wrong_password(self):
|
def test_access_denied_with_wrong_password(self):
|
||||||
req = requests.get(
|
req = requests.get(
|
||||||
_url(const.URL_API_STATES_ENTITY.format("test")),
|
_url(const.URL_API),
|
||||||
headers={const.HTTP_HEADER_HA_AUTH: 'wrongpassword'})
|
headers={const.HTTP_HEADER_HA_AUTH: 'wrongpassword'})
|
||||||
|
|
||||||
self.assertEqual(401, req.status_code)
|
self.assertEqual(401, req.status_code)
|
||||||
|
|
||||||
|
def test_access_with_password_in_url(self):
|
||||||
|
req = requests.get(
|
||||||
|
"{}?api_password={}".format(_url(const.URL_API), API_PASSWORD))
|
||||||
|
|
||||||
|
self.assertEqual(200, req.status_code)
|
||||||
|
|
||||||
|
def test_access_via_session(self):
|
||||||
|
session = requests.Session()
|
||||||
|
req = session.get(_url(const.URL_API), headers=HA_HEADERS)
|
||||||
|
self.assertEqual(200, req.status_code)
|
||||||
|
|
||||||
|
req = session.get(_url(const.URL_API))
|
||||||
|
self.assertEqual(200, req.status_code)
|
||||||
|
|
||||||
def test_api_list_state_entities(self):
|
def test_api_list_state_entities(self):
|
||||||
""" Test if the debug interface allows us to list state entities. """
|
""" Test if the debug interface allows us to list state entities. """
|
||||||
req = requests.get(_url(const.URL_API_STATES),
|
req = requests.get(_url(const.URL_API_STATES),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user