mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Allow for running Home Assistant without password
This commit is contained in:
parent
50eecd11c1
commit
ed05ff6fd9
@ -45,7 +45,7 @@ pip3 install -r requirements.txt
|
||||
python3 -m homeassistant --open-ui
|
||||
```
|
||||
|
||||
This will start the Home Assistant server and create an initial configuration file in `config/home-assistant.conf` that is setup for demo mode. It will launch its web interface on [http://127.0.0.1:8123](http://127.0.0.1:8123). The default password is 'password'.
|
||||
This will start the Home Assistant server and launch its webinterface. By default Home Assistant looks for the configuration file `config/home-assistant.conf`. A standard configuration file will be written if none exists.
|
||||
|
||||
If you're using Docker, you can use
|
||||
|
||||
@ -53,6 +53,8 @@ If you're using Docker, you can use
|
||||
docker run -d --name="home-assistant" -v /path/to/homeassistant/config:/config -v /etc/localtime:/etc/localtime:ro --net=host balloob/home-assistant
|
||||
```
|
||||
|
||||
After you have launched the Docker image, navigate to its web interface on [http://127.0.0.1:8123](http://127.0.0.1:8123).
|
||||
|
||||
After you got the demo mode running it is time to enable some real components and get started. An example configuration file has been provided in [/config/home-assistant.conf.example](https://github.com/balloob/home-assistant/blob/master/config/home-assistant.conf.example).
|
||||
|
||||
*Note:* you can append `?api_password=YOUR_PASSWORD` to the url of the web interface to log in automatically.
|
||||
|
@ -54,9 +54,8 @@ def ensure_config_path(config_dir):
|
||||
if not os.path.isfile(config_path):
|
||||
try:
|
||||
with open(config_path, 'w') as conf:
|
||||
conf.write("[http]\n")
|
||||
conf.write("api_password=password\n\n")
|
||||
conf.write("[demo]\n")
|
||||
conf.write("[http]\n\n")
|
||||
conf.write("[demo]\n\n")
|
||||
except IOError:
|
||||
print(('Fatal Error: No configuration file found and unable '
|
||||
'to write a default one to {}').format(config_path))
|
||||
|
@ -86,7 +86,7 @@ import homeassistant as ha
|
||||
from homeassistant.const import (
|
||||
SERVER_PORT, URL_API, URL_API_STATES, URL_API_EVENTS, URL_API_SERVICES,
|
||||
URL_API_EVENT_FORWARD, URL_API_STATES_ENTITY, AUTH_HEADER)
|
||||
from homeassistant.helpers import validate_config, TrackStates
|
||||
from homeassistant.helpers import TrackStates
|
||||
import homeassistant.remote as rem
|
||||
import homeassistant.util as util
|
||||
from . import frontend
|
||||
@ -117,13 +117,18 @@ DATA_API_PASSWORD = 'api_password'
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
def setup(hass, config=None):
|
||||
""" Sets up the HTTP API and debug interface. """
|
||||
|
||||
if not validate_config(config, {DOMAIN: [CONF_API_PASSWORD]}, _LOGGER):
|
||||
return False
|
||||
if config is None or DOMAIN not in config:
|
||||
config = {DOMAIN: {}}
|
||||
|
||||
api_password = config[DOMAIN][CONF_API_PASSWORD]
|
||||
api_password = config[DOMAIN].get(CONF_API_PASSWORD)
|
||||
|
||||
no_password_set = api_password is None
|
||||
|
||||
if no_password_set:
|
||||
api_password = util.get_random_string()
|
||||
|
||||
# If no server host is given, accept all incoming requests
|
||||
server_host = config[DOMAIN].get(CONF_SERVER_HOST, '0.0.0.0')
|
||||
@ -132,9 +137,9 @@ def setup(hass, config):
|
||||
|
||||
development = config[DOMAIN].get(CONF_DEVELOPMENT, "") == "1"
|
||||
|
||||
server = HomeAssistantHTTPServer((server_host, server_port),
|
||||
RequestHandler, hass, api_password,
|
||||
development)
|
||||
server = HomeAssistantHTTPServer(
|
||||
(server_host, server_port), RequestHandler, hass, api_password,
|
||||
development, no_password_set)
|
||||
|
||||
hass.bus.listen_once(
|
||||
ha.EVENT_HOMEASSISTANT_START,
|
||||
@ -155,13 +160,14 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, server_address, request_handler_class,
|
||||
hass, api_password, development=False):
|
||||
hass, api_password, development, no_password_set):
|
||||
super().__init__(server_address, request_handler_class)
|
||||
|
||||
self.server_address = server_address
|
||||
self.hass = hass
|
||||
self.api_password = api_password
|
||||
self.development = development
|
||||
self.no_password_set = no_password_set
|
||||
|
||||
# We will lazy init this one if needed
|
||||
self.event_forwarder = None
|
||||
@ -270,10 +276,13 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||
"Error parsing JSON", HTTP_UNPROCESSABLE_ENTITY)
|
||||
return
|
||||
|
||||
api_password = self.headers.get(AUTH_HEADER)
|
||||
if self.server.no_password_set:
|
||||
api_password = self.server.api_password
|
||||
else:
|
||||
api_password = self.headers.get(AUTH_HEADER)
|
||||
|
||||
if not api_password and DATA_API_PASSWORD in data:
|
||||
api_password = data[DATA_API_PASSWORD]
|
||||
if not api_password and DATA_API_PASSWORD in data:
|
||||
api_password = data[DATA_API_PASSWORD]
|
||||
|
||||
if '_METHOD' in data:
|
||||
method = data.pop('_METHOD')
|
||||
@ -357,6 +366,10 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||
else:
|
||||
app_url = "frontend-{}.html".format(frontend.VERSION)
|
||||
|
||||
# auto login if no password was set, else check api_password param
|
||||
auth = (self.server.api_password if self.server.no_password_set
|
||||
else data.get('api_password', ''))
|
||||
|
||||
write(("<!doctype html>"
|
||||
"<html>"
|
||||
"<head><title>Home Assistant</title>"
|
||||
@ -375,7 +388,7 @@ class RequestHandler(SimpleHTTPRequestHandler):
|
||||
" src='/static/webcomponents.min.js'></script>"
|
||||
"<link rel='import' href='/static/{}' />"
|
||||
"<splash-login auth='{}'></splash-login>"
|
||||
"</body></html>").format(app_url, data.get('api_password', '')))
|
||||
"</body></html>").format(app_url, auth))
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def _handle_get_api(self, path_match, data):
|
||||
|
@ -112,17 +112,11 @@ class HomeAssistant(ha.HomeAssistant):
|
||||
self.states = StateMachine(self.bus, self.remote_api)
|
||||
|
||||
def start(self):
|
||||
# If there is no local API setup but we do want to connect with remote
|
||||
# We create a random password and set up a local api
|
||||
# Ensure a local API exists to connect with remote
|
||||
if self.local_api is None:
|
||||
import homeassistant.components.http as http
|
||||
import random
|
||||
|
||||
# pylint: disable=too-many-format-args
|
||||
random_password = '{:30}'.format(random.randrange(16**30))
|
||||
|
||||
http.setup(
|
||||
self, {http.DOMAIN: {http.CONF_API_PASSWORD: random_password}})
|
||||
http.setup(self)
|
||||
|
||||
ha.Timer(self)
|
||||
|
||||
|
@ -12,6 +12,8 @@ from datetime import datetime, timedelta
|
||||
import re
|
||||
import enum
|
||||
import socket
|
||||
import random
|
||||
import string
|
||||
from functools import wraps
|
||||
|
||||
RE_SANITIZE_FILENAME = re.compile(r'(~|\.\.|/|\\)')
|
||||
@ -134,16 +136,16 @@ def convert(value, to_type, default=None):
|
||||
def ensure_unique_string(preferred_string, current_strings):
|
||||
""" Returns a string that is not present in current_strings.
|
||||
If preferred string exists will append _2, _3, .. """
|
||||
string = preferred_string
|
||||
test_string = preferred_string
|
||||
current_strings = list(current_strings)
|
||||
|
||||
tries = 1
|
||||
|
||||
while string in current_strings:
|
||||
while test_string in current_strings:
|
||||
tries += 1
|
||||
string = "{}_{}".format(preferred_string, tries)
|
||||
test_string = "{}_{}".format(preferred_string, tries)
|
||||
|
||||
return string
|
||||
return test_string
|
||||
|
||||
|
||||
# Taken from: http://stackoverflow.com/a/11735897
|
||||
@ -163,6 +165,15 @@ def get_local_ip():
|
||||
return socket.gethostbyname(socket.gethostname())
|
||||
|
||||
|
||||
# Taken from http://stackoverflow.com/a/23728630
|
||||
def get_random_string(length=10):
|
||||
""" Returns a random string with letters and digits. """
|
||||
generator = random.SystemRandom()
|
||||
source_chars = string.ascii_letters + string.digits
|
||||
|
||||
return ''.join(generator.choice(source_chars) for _ in range(length))
|
||||
|
||||
|
||||
class OrderedEnum(enum.Enum):
|
||||
""" Taken from Python 3.4.0 docs. """
|
||||
# pylint: disable=no-init, too-few-public-methods
|
||||
|
@ -58,11 +58,6 @@ def tearDownModule(): # pylint: disable=invalid-name
|
||||
class TestHTTP(unittest.TestCase):
|
||||
""" Test the HTTP debug interface and API. """
|
||||
|
||||
def test_setup(self):
|
||||
""" Test http.setup. """
|
||||
self.assertFalse(http.setup(hass, {}))
|
||||
self.assertFalse(http.setup(hass, {http.DOMAIN: {}}))
|
||||
|
||||
def test_frontend_and_static(self):
|
||||
""" Tests if we can get the frontend. """
|
||||
req = requests.get(_url(""))
|
||||
|
@ -68,12 +68,13 @@ class TestRemoteMethods(unittest.TestCase):
|
||||
""" Test Python API validate_api. """
|
||||
self.assertEqual(remote.APIStatus.OK, remote.validate_api(master_api))
|
||||
|
||||
self.assertEqual(remote.APIStatus.INVALID_PASSWORD,
|
||||
remote.validate_api(
|
||||
remote.API("127.0.0.1", API_PASSWORD + "A")))
|
||||
self.assertEqual(
|
||||
remote.APIStatus.INVALID_PASSWORD,
|
||||
remote.validate_api(
|
||||
remote.API("127.0.0.1", API_PASSWORD + "A", 8122)))
|
||||
|
||||
self.assertEqual(remote.APIStatus.CANNOT_CONNECT,
|
||||
remote.validate_api(broken_api))
|
||||
self.assertEqual(
|
||||
remote.APIStatus.CANNOT_CONNECT, remote.validate_api(broken_api))
|
||||
|
||||
def test_get_event_listeners(self):
|
||||
""" Test Python API get_event_listeners. """
|
||||
|
Loading…
x
Reference in New Issue
Block a user