mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-19 23:26:29 +00:00
Fix merge conflicts
This commit is contained in:
commit
a6c424b7c8
45
API.md
45
API.md
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
Interface for Home Assistant to control things from supervisor.
|
Interface for Home Assistant to control things from supervisor.
|
||||||
|
|
||||||
On error:
|
On error / Code 400:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -13,7 +13,7 @@ On error:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
On success:
|
On success / Code 200:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@ -22,6 +22,8 @@ On success:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For access to API you need set the `X-HASSIO-KEY` they will be available for Add-ons/HomeAssistant with envoriment `HASSIO_TOKEN`.
|
||||||
|
|
||||||
### Hass.io
|
### Hass.io
|
||||||
|
|
||||||
- GET `/supervisor/ping`
|
- GET `/supervisor/ping`
|
||||||
@ -99,44 +101,7 @@ Output is the raw docker log.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Security
|
### Snapshot
|
||||||
|
|
||||||
- GET `/security/info`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"initialize": "bool",
|
|
||||||
"totp": "bool"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- POST `/security/options`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"password": "xy"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- POST `/security/totp`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"password": "xy"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Return QR-Code
|
|
||||||
|
|
||||||
- POST `/security/session`
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"password": "xy",
|
|
||||||
"totp": "null|123456"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Backup/Snapshot
|
|
||||||
|
|
||||||
- GET `/snapshots`
|
- GET `/snapshots`
|
||||||
|
|
||||||
|
10
README.md
10
README.md
@ -1,8 +1,12 @@
|
|||||||
# Hass.io
|
# Hass.io
|
||||||
|
|
||||||
### First private cloud solution for home automation.
|
## First private cloud solution for home automation
|
||||||
|
|
||||||
Hass.io is a Docker based system for managing your Home Assistant installation and related applications. The system is controlled via Home Assistant which communicates with the supervisor. The supervisor provides an API to manage the installation. This includes changing network settings or installing and updating software.
|
Hass.io is a Docker-based system for managing your Home Assistant installation
|
||||||
|
and related applications. The system is controlled via Home Assistant which
|
||||||
|
communicates with the Supervisor. The Supervisor provides an API to manage the
|
||||||
|
installation. This includes changing network settings or installing
|
||||||
|
and updating software.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -11,4 +15,4 @@ Hass.io is a Docker based system for managing your Home Assistant installation a
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Installation instructions can be found at [https://home-assistant.io/hassio](https://home-assistant.io/hassio).
|
Installation instructions can be found at <https://home-assistant.io/hassio>.
|
||||||
|
@ -160,7 +160,7 @@ class Addon(CoreSysAttributes):
|
|||||||
return self._mesh[ATTR_TIMEOUT]
|
return self._mesh[ATTR_TIMEOUT]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_token(self):
|
def uuid(self):
|
||||||
"""Return a API token for this add-on."""
|
"""Return a API token for this add-on."""
|
||||||
if self.is_installed:
|
if self.is_installed:
|
||||||
return self._data.user[self._id][ATTR_UUID]
|
return self._data.user[self._id][ATTR_UUID]
|
||||||
|
@ -10,7 +10,6 @@ from .host import APIHost
|
|||||||
from .network import APINetwork
|
from .network import APINetwork
|
||||||
from .proxy import APIProxy
|
from .proxy import APIProxy
|
||||||
from .supervisor import APISupervisor
|
from .supervisor import APISupervisor
|
||||||
from .security import APISecurity
|
|
||||||
from .snapshots import APISnapshots
|
from .snapshots import APISnapshots
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
|
|
||||||
@ -38,7 +37,6 @@ class RestAPI(CoreSysAttributes):
|
|||||||
self._register_panel()
|
self._register_panel()
|
||||||
self._register_addons()
|
self._register_addons()
|
||||||
self._register_snapshots()
|
self._register_snapshots()
|
||||||
self._register_security()
|
|
||||||
self._register_network()
|
self._register_network()
|
||||||
|
|
||||||
def _register_host(self):
|
def _register_host(self):
|
||||||
@ -102,12 +100,14 @@ class RestAPI(CoreSysAttributes):
|
|||||||
'/homeassistant/api/websocket', api_proxy.websocket)
|
'/homeassistant/api/websocket', api_proxy.websocket)
|
||||||
self.webapp.router.add_get(
|
self.webapp.router.add_get(
|
||||||
'/homeassistant/websocket', api_proxy.websocket)
|
'/homeassistant/websocket', api_proxy.websocket)
|
||||||
|
self.webapp.router.add_get(
|
||||||
|
'/homeassistant/api/stream', api_proxy.stream)
|
||||||
self.webapp.router.add_post(
|
self.webapp.router.add_post(
|
||||||
'/homeassistant/api/{path:.+}', api_proxy.api)
|
'/homeassistant/api/{path:.+}', api_proxy.api)
|
||||||
self.webapp.router.add_get(
|
self.webapp.router.add_get(
|
||||||
'/homeassistant/api/{path:.+}', api_proxy.api)
|
'/homeassistant/api/{path:.+}', api_proxy.api)
|
||||||
self.webapp.router.add_get(
|
self.webapp.router.add_get(
|
||||||
'/homeassistant/api', api_proxy.api)
|
'/homeassistant/api/', api_proxy.api)
|
||||||
|
|
||||||
def _register_addons(self):
|
def _register_addons(self):
|
||||||
"""Register homeassistant function."""
|
"""Register homeassistant function."""
|
||||||
@ -138,16 +138,6 @@ class RestAPI(CoreSysAttributes):
|
|||||||
self.webapp.router.add_post('/addons/{addon}/stdin', api_addons.stdin)
|
self.webapp.router.add_post('/addons/{addon}/stdin', api_addons.stdin)
|
||||||
self.webapp.router.add_get('/addons/{addon}/stats', api_addons.stats)
|
self.webapp.router.add_get('/addons/{addon}/stats', api_addons.stats)
|
||||||
|
|
||||||
def _register_security(self):
|
|
||||||
"""Register security function."""
|
|
||||||
api_security = APISecurity()
|
|
||||||
api_security.coresys = self.coresys
|
|
||||||
|
|
||||||
self.webapp.router.add_get('/security/info', api_security.info)
|
|
||||||
self.webapp.router.add_post('/security/options', api_security.options)
|
|
||||||
self.webapp.router.add_post('/security/totp', api_security.totp)
|
|
||||||
self.webapp.router.add_post('/security/session', api_security.session)
|
|
||||||
|
|
||||||
def _register_snapshots(self):
|
def _register_snapshots(self):
|
||||||
"""Register snapshots function."""
|
"""Register snapshots function."""
|
||||||
api_snapshots = APISnapshots()
|
api_snapshots = APISnapshots()
|
||||||
|
@ -267,7 +267,7 @@ class APIAddons(CoreSysAttributes):
|
|||||||
"""Write to stdin of addon."""
|
"""Write to stdin of addon."""
|
||||||
addon = self._extract_addon(request)
|
addon = self._extract_addon(request)
|
||||||
if not addon.with_stdin:
|
if not addon.with_stdin:
|
||||||
raise RuntimeError("STDIN not supported by addons")
|
raise RuntimeError("STDIN not supported by addon")
|
||||||
|
|
||||||
data = await request.read()
|
data = await request.read()
|
||||||
return await asyncio.shield(addon.write_stdin(data), loop=self._loop)
|
return await asyncio.shield(addon.write_stdin(data), loop=self._loop)
|
||||||
|
@ -25,6 +25,7 @@ class APIProxy(CoreSysAttributes):
|
|||||||
data = None
|
data = None
|
||||||
headers = {}
|
headers = {}
|
||||||
method = getattr(self._websession_ssl, request.method.lower())
|
method = getattr(self._websession_ssl, request.method.lower())
|
||||||
|
params = request.query or None
|
||||||
|
|
||||||
# read data
|
# read data
|
||||||
with async_timeout.timeout(30, loop=self._loop):
|
with async_timeout.timeout(30, loop=self._loop):
|
||||||
@ -42,7 +43,8 @@ class APIProxy(CoreSysAttributes):
|
|||||||
headers = None
|
headers = None
|
||||||
|
|
||||||
client = await method(
|
client = await method(
|
||||||
url, data=data, headers=headers, timeout=timeout
|
url, data=data, headers=headers, timeout=timeout,
|
||||||
|
params=params
|
||||||
)
|
)
|
||||||
|
|
||||||
return client
|
return client
|
||||||
@ -55,48 +57,46 @@ class APIProxy(CoreSysAttributes):
|
|||||||
|
|
||||||
raise HTTPBadGateway()
|
raise HTTPBadGateway()
|
||||||
|
|
||||||
|
async def stream(self, request):
|
||||||
|
"""Proxy HomeAssistant EventStream Requests."""
|
||||||
|
_LOGGER.info("Home-Assistant EventStream start")
|
||||||
|
client = await self._api_client(request, 'stream', timeout=None)
|
||||||
|
|
||||||
|
response = web.StreamResponse()
|
||||||
|
response.content_type = request.headers.get(CONTENT_TYPE)
|
||||||
|
try:
|
||||||
|
await response.prepare(request)
|
||||||
|
while True:
|
||||||
|
data = await client.content.read(10)
|
||||||
|
if not data:
|
||||||
|
await response.write_eof()
|
||||||
|
break
|
||||||
|
response.write(data)
|
||||||
|
|
||||||
|
except aiohttp.ClientError:
|
||||||
|
await response.write_eof()
|
||||||
|
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
_LOGGER.info("Home-Assistant EventStream close")
|
||||||
|
|
||||||
async def api(self, request):
|
async def api(self, request):
|
||||||
"""Proxy HomeAssistant API Requests."""
|
"""Proxy HomeAssistant API Requests."""
|
||||||
path = request.match_info.get('path', '')
|
path = request.match_info.get('path', '')
|
||||||
|
|
||||||
# API stream
|
|
||||||
if path.startswith("stream"):
|
|
||||||
_LOGGER.info("Home-Assistant Event-Stream start")
|
|
||||||
client = await self._api_client(request, path, timeout=None)
|
|
||||||
|
|
||||||
response = web.StreamResponse()
|
|
||||||
response.content_type = request.headers.get(CONTENT_TYPE)
|
|
||||||
try:
|
|
||||||
await response.prepare(request)
|
|
||||||
while True:
|
|
||||||
data = await client.content.read(10)
|
|
||||||
if not data:
|
|
||||||
await response.write_eof()
|
|
||||||
break
|
|
||||||
response.write(data)
|
|
||||||
|
|
||||||
except aiohttp.ClientError:
|
|
||||||
await response.write_eof()
|
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
finally:
|
|
||||||
client.close()
|
|
||||||
|
|
||||||
_LOGGER.info("Home-Assistant Event-Stream close")
|
|
||||||
|
|
||||||
# Normal request
|
# Normal request
|
||||||
else:
|
_LOGGER.info("Home-Assistant /api/%s request", path)
|
||||||
_LOGGER.info("Home-Assistant '/api/%s' request", path)
|
client = await self._api_client(request, path)
|
||||||
client = await self._api_client(request, path)
|
|
||||||
|
|
||||||
data = await client.read()
|
data = await client.read()
|
||||||
return web.Response(
|
return web.Response(
|
||||||
body=data,
|
body=data,
|
||||||
status=client.status,
|
status=client.status,
|
||||||
content_type=client.content_type
|
content_type=client.content_type
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _websocket_client(self):
|
async def _websocket_client(self):
|
||||||
"""Initialize a websocket api connection."""
|
"""Initialize a websocket api connection."""
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
"""Init file for HassIO security rest api."""
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import io
|
|
||||||
import logging
|
|
||||||
import hashlib
|
|
||||||
import os
|
|
||||||
|
|
||||||
from aiohttp import web
|
|
||||||
import voluptuous as vol
|
|
||||||
import pyotp
|
|
||||||
import pyqrcode
|
|
||||||
|
|
||||||
from .utils import api_process, api_validate, hash_password
|
|
||||||
from ..const import ATTR_INITIALIZE, ATTR_PASSWORD, ATTR_TOTP, ATTR_SESSION
|
|
||||||
from ..coresys import CoreSysAttributes
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
SCHEMA_PASSWORD = vol.Schema({
|
|
||||||
vol.Required(ATTR_PASSWORD): vol.Coerce(str),
|
|
||||||
})
|
|
||||||
|
|
||||||
SCHEMA_SESSION = SCHEMA_PASSWORD.extend({
|
|
||||||
vol.Optional(ATTR_TOTP, default=None): vol.Coerce(str),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class APISecurity(CoreSysAttributes):
|
|
||||||
"""Handle rest api for security functions."""
|
|
||||||
|
|
||||||
def _check_password(self, body):
|
|
||||||
"""Check if password is valid and security is initialize."""
|
|
||||||
if not self._config.security_initialize:
|
|
||||||
raise RuntimeError("First set a password")
|
|
||||||
|
|
||||||
password = hash_password(body[ATTR_PASSWORD])
|
|
||||||
if password != self._config.security_password:
|
|
||||||
raise RuntimeError("Wrong password")
|
|
||||||
|
|
||||||
@api_process
|
|
||||||
async def info(self, request):
|
|
||||||
"""Return host information."""
|
|
||||||
return {
|
|
||||||
ATTR_INITIALIZE: self._config.security_initialize,
|
|
||||||
ATTR_TOTP: self._config.security_totp is not None,
|
|
||||||
}
|
|
||||||
|
|
||||||
@api_process
|
|
||||||
async def options(self, request):
|
|
||||||
"""Set options / password."""
|
|
||||||
body = await api_validate(SCHEMA_PASSWORD, request)
|
|
||||||
|
|
||||||
if self._config.security_initialize:
|
|
||||||
raise RuntimeError("Password is already set!")
|
|
||||||
|
|
||||||
self._config.security_password = hash_password(body[ATTR_PASSWORD])
|
|
||||||
self._config.security_initialize = True
|
|
||||||
return True
|
|
||||||
|
|
||||||
@api_process
|
|
||||||
async def totp(self, request):
|
|
||||||
"""Set and initialze TOTP."""
|
|
||||||
body = await api_validate(SCHEMA_PASSWORD, request)
|
|
||||||
self._check_password(body)
|
|
||||||
|
|
||||||
# generate TOTP
|
|
||||||
totp_init_key = pyotp.random_base32()
|
|
||||||
totp = pyotp.TOTP(totp_init_key)
|
|
||||||
|
|
||||||
# init qrcode
|
|
||||||
buff = io.BytesIO()
|
|
||||||
|
|
||||||
qrcode = pyqrcode.create(totp.provisioning_uri("Hass.IO"))
|
|
||||||
qrcode.svg(buff)
|
|
||||||
|
|
||||||
# finish
|
|
||||||
self._config.security_totp = totp_init_key
|
|
||||||
return web.Response(body=buff.getvalue(), content_type='image/svg+xml')
|
|
||||||
|
|
||||||
@api_process
|
|
||||||
async def session(self, request):
|
|
||||||
"""Set and initialze session."""
|
|
||||||
body = await api_validate(SCHEMA_SESSION, request)
|
|
||||||
self._check_password(body)
|
|
||||||
|
|
||||||
# check TOTP
|
|
||||||
if self._config.security_totp:
|
|
||||||
totp = pyotp.TOTP(self._config.security_totp)
|
|
||||||
if body[ATTR_TOTP] != totp.now():
|
|
||||||
raise RuntimeError("Invalid TOTP token!")
|
|
||||||
|
|
||||||
# create session
|
|
||||||
valid_until = datetime.now() + timedelta(days=1)
|
|
||||||
session = hashlib.sha256(os.urandom(54)).hexdigest()
|
|
||||||
|
|
||||||
# store session
|
|
||||||
self._config.add_security_session(session, valid_until)
|
|
||||||
return {ATTR_SESSION: session}
|
|
@ -5,8 +5,7 @@ import os
|
|||||||
from pathlib import Path, PurePath
|
from pathlib import Path, PurePath
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
FILE_HASSIO_CONFIG, HASSIO_DATA, ATTR_SECURITY, ATTR_SESSIONS,
|
FILE_HASSIO_CONFIG, HASSIO_DATA, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST,
|
||||||
ATTR_PASSWORD, ATTR_TOTP, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST,
|
|
||||||
ATTR_AUDIO_INPUT, ATTR_AUDIO_OUTPUT, ATTR_LAST_BOOT, ATTR_WAIT_BOOT)
|
ATTR_AUDIO_INPUT, ATTR_AUDIO_OUTPUT, ATTR_LAST_BOOT, ATTR_WAIT_BOOT)
|
||||||
from .utils.dt import parse_datetime
|
from .utils.dt import parse_datetime
|
||||||
from .utils.json import JsonConfig
|
from .utils.json import JsonConfig
|
||||||
@ -181,59 +180,6 @@ class CoreConfig(JsonConfig):
|
|||||||
self._data[ATTR_ADDONS_CUSTOM_LIST].remove(repo)
|
self._data[ATTR_ADDONS_CUSTOM_LIST].remove(repo)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@property
|
|
||||||
def security_initialize(self):
|
|
||||||
"""Return is security was initialize."""
|
|
||||||
return self._data[ATTR_SECURITY]
|
|
||||||
|
|
||||||
@security_initialize.setter
|
|
||||||
def security_initialize(self, value):
|
|
||||||
"""Set is security initialize."""
|
|
||||||
self._data[ATTR_SECURITY] = value
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def security_totp(self):
|
|
||||||
"""Return the TOTP key."""
|
|
||||||
return self._data.get(ATTR_TOTP)
|
|
||||||
|
|
||||||
@security_totp.setter
|
|
||||||
def security_totp(self, value):
|
|
||||||
"""Set the TOTP key."""
|
|
||||||
self._data[ATTR_TOTP] = value
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def security_password(self):
|
|
||||||
"""Return the password key."""
|
|
||||||
return self._data.get(ATTR_PASSWORD)
|
|
||||||
|
|
||||||
@security_password.setter
|
|
||||||
def security_password(self, value):
|
|
||||||
"""Set the password key."""
|
|
||||||
self._data[ATTR_PASSWORD] = value
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def security_sessions(self):
|
|
||||||
"""Return api sessions."""
|
|
||||||
return {
|
|
||||||
session: parse_datetime(until) for
|
|
||||||
session, until in self._data[ATTR_SESSIONS].items()
|
|
||||||
}
|
|
||||||
|
|
||||||
def add_security_session(self, session, valid):
|
|
||||||
"""Set the a new session."""
|
|
||||||
self._data[ATTR_SESSIONS].update(
|
|
||||||
{session: valid.isoformat()}
|
|
||||||
)
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def drop_security_session(self, session):
|
|
||||||
"""Delete the a session."""
|
|
||||||
self._data[ATTR_SESSIONS].pop(session, None)
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def audio_output(self):
|
def audio_output(self):
|
||||||
"""Return ALSA audio output card,dev."""
|
"""Return ALSA audio output card,dev."""
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ipaddress import ip_network
|
from ipaddress import ip_network
|
||||||
|
|
||||||
HASSIO_VERSION = '0.81'
|
HASSIO_VERSION = '0.82'
|
||||||
|
|
||||||
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
|
URL_HASSIO_VERSION = ('https://raw.githubusercontent.com/home-assistant/'
|
||||||
'hassio/{}/version.json')
|
'hassio/{}/version.json')
|
||||||
|
@ -82,7 +82,7 @@ class DockerAddon(DockerInterface):
|
|||||||
|
|
||||||
# Set api token if any API access is needed
|
# Set api token if any API access is needed
|
||||||
if self.addon.access_hassio_api or self.addon.access_homeassistant_api:
|
if self.addon.access_hassio_api or self.addon.access_homeassistant_api:
|
||||||
addon_env['API_TOKEN'] = self.addon.api_token
|
addon_env['HASSIO_TOKEN'] = self.addon.uuid
|
||||||
|
|
||||||
return {
|
return {
|
||||||
**addon_env,
|
**addon_env,
|
||||||
|
@ -54,6 +54,7 @@ class DockerHomeAssistant(DockerInterface):
|
|||||||
environment={
|
environment={
|
||||||
'HASSIO': self._docker.network.supervisor,
|
'HASSIO': self._docker.network.supervisor,
|
||||||
'TZ': self._config.timezone,
|
'TZ': self._config.timezone,
|
||||||
|
'HASSIO_TOKEN': self._homeassistant.uuid,
|
||||||
},
|
},
|
||||||
volumes={
|
volumes={
|
||||||
str(self._config.path_extern_config):
|
str(self._config.path_extern_config):
|
||||||
|
@ -9,7 +9,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class DockerNetwork(object):
|
class DockerNetwork(object):
|
||||||
"""Internal HassIO Network."""
|
"""Internal HassIO Network.
|
||||||
|
|
||||||
|
This class is not AsyncIO safe!
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, dock):
|
def __init__(self, dock):
|
||||||
"""Initialize internal hassio network."""
|
"""Initialize internal hassio network."""
|
||||||
|
@ -8,7 +8,7 @@ import aiohttp
|
|||||||
from aiohttp.hdrs import CONTENT_TYPE
|
from aiohttp.hdrs import CONTENT_TYPE
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
FILE_HASSIO_HOMEASSISTANT, ATTR_IMAGE, ATTR_LAST_VERSION,
|
FILE_HASSIO_HOMEASSISTANT, ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_UUID,
|
||||||
ATTR_BOOT, ATTR_PASSWORD, ATTR_PORT, ATTR_SSL, ATTR_WATCHDOG,
|
ATTR_BOOT, ATTR_PASSWORD, ATTR_PORT, ATTR_SSL, ATTR_WATCHDOG,
|
||||||
HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
|
HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
|
||||||
from .coresys import CoreSysAttributes
|
from .coresys import CoreSysAttributes
|
||||||
@ -143,6 +143,11 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
"""Set home-assistant boot options."""
|
"""Set home-assistant boot options."""
|
||||||
self._data[ATTR_BOOT] = value
|
self._data[ATTR_BOOT] = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uuid(self):
|
||||||
|
"""Return a UUID of this HomeAssistant."""
|
||||||
|
return self._data[ATTR_UUID]
|
||||||
|
|
||||||
async def install_landingpage(self):
|
async def install_landingpage(self):
|
||||||
"""Install a landingpage."""
|
"""Install a landingpage."""
|
||||||
_LOGGER.info("Setup HomeAssistant landingpage")
|
_LOGGER.info("Setup HomeAssistant landingpage")
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Multible tasks."""
|
"""Multible tasks."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .coresys import CoreSysAttributes
|
from .coresys import CoreSysAttributes
|
||||||
@ -22,8 +21,6 @@ class Tasks(CoreSysAttributes):
|
|||||||
RUN_WATCHDOG_HOMEASSISTANT_DOCKER = 15
|
RUN_WATCHDOG_HOMEASSISTANT_DOCKER = 15
|
||||||
RUN_WATCHDOG_HOMEASSISTANT_API = 300
|
RUN_WATCHDOG_HOMEASSISTANT_API = 300
|
||||||
|
|
||||||
RUN_CLEANUP_API_SESSIONS = 900
|
|
||||||
|
|
||||||
def __init__(self, coresys):
|
def __init__(self, coresys):
|
||||||
"""Initialize Tasks."""
|
"""Initialize Tasks."""
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
@ -55,13 +52,6 @@ class Tasks(CoreSysAttributes):
|
|||||||
|
|
||||||
_LOGGER.info("All core tasks are scheduled")
|
_LOGGER.info("All core tasks are scheduled")
|
||||||
|
|
||||||
async def _cleanup_sessions(self):
|
|
||||||
"""Cleanup old api sessions."""
|
|
||||||
now = datetime.now()
|
|
||||||
for session, until_valid in self._config.security_sessions.items():
|
|
||||||
if now >= until_valid:
|
|
||||||
self._config.drop_security_session(session)
|
|
||||||
|
|
||||||
async def _update_addons(self):
|
async def _update_addons(self):
|
||||||
"""Check if a update is available of a addon and update it."""
|
"""Check if a update is available of a addon and update it."""
|
||||||
tasks = []
|
tasks = []
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
"""Validate functions."""
|
"""Validate functions."""
|
||||||
import voluptuous as vol
|
import uuid
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_SESSIONS, ATTR_PASSWORD, ATTR_TOTP,
|
ATTR_IMAGE, ATTR_LAST_VERSION, ATTR_BETA_CHANNEL, ATTR_TIMEZONE,
|
||||||
ATTR_SECURITY, ATTR_BETA_CHANNEL, ATTR_TIMEZONE, ATTR_ADDONS_CUSTOM_LIST,
|
ATTR_ADDONS_CUSTOM_LIST, ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT,
|
||||||
ATTR_AUDIO_OUTPUT, ATTR_AUDIO_INPUT, ATTR_HOMEASSISTANT, ATTR_HASSIO,
|
ATTR_PASSWORD, ATTR_HOMEASSISTANT, ATTR_HASSIO, ATTR_BOOT, ATTR_LAST_BOOT,
|
||||||
ATTR_BOOT, ATTR_LAST_BOOT, ATTR_SSL, ATTR_PORT, ATTR_WATCHDOG,
|
ATTR_SSL, ATTR_PORT, ATTR_WATCHDOG, ATTR_WAIT_BOOT, ATTR_UUID)
|
||||||
ATTR_WAIT_BOOT)
|
|
||||||
|
|
||||||
|
|
||||||
NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
|
NETWORK_PORT = vol.All(vol.Coerce(int), vol.Range(min=1, max=65535))
|
||||||
@ -59,6 +59,8 @@ DOCKER_PORTS = vol.Schema({
|
|||||||
|
|
||||||
# pylint: disable=no-value-for-parameter
|
# pylint: disable=no-value-for-parameter
|
||||||
SCHEMA_HASS_CONFIG = vol.Schema({
|
SCHEMA_HASS_CONFIG = vol.Schema({
|
||||||
|
vol.Optional(ATTR_UUID, default=lambda: uuid.uuid4().hex):
|
||||||
|
vol.Match(r"^[0-9a-f]{32}$"),
|
||||||
vol.Optional(ATTR_BOOT, default=True): vol.Boolean(),
|
vol.Optional(ATTR_BOOT, default=True): vol.Boolean(),
|
||||||
vol.Inclusive(ATTR_IMAGE, 'custom_hass'): vol.Coerce(str),
|
vol.Inclusive(ATTR_IMAGE, 'custom_hass'): vol.Coerce(str),
|
||||||
vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str),
|
vol.Inclusive(ATTR_LAST_VERSION, 'custom_hass'): vol.Coerce(str),
|
||||||
@ -84,11 +86,6 @@ SCHEMA_HASSIO_CONFIG = vol.Schema({
|
|||||||
vol.Optional(ATTR_ADDONS_CUSTOM_LIST, default=[
|
vol.Optional(ATTR_ADDONS_CUSTOM_LIST, default=[
|
||||||
"https://github.com/hassio-addons/repository",
|
"https://github.com/hassio-addons/repository",
|
||||||
]): [vol.Url()],
|
]): [vol.Url()],
|
||||||
vol.Optional(ATTR_SECURITY, default=False): vol.Boolean(),
|
|
||||||
vol.Optional(ATTR_TOTP): vol.Coerce(str),
|
|
||||||
vol.Optional(ATTR_PASSWORD): vol.Coerce(str),
|
|
||||||
vol.Optional(ATTR_SESSIONS, default={}):
|
|
||||||
vol.Schema({vol.Coerce(str): vol.Coerce(str)}),
|
|
||||||
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_CHANNEL,
|
vol.Optional(ATTR_AUDIO_OUTPUT): ALSA_CHANNEL,
|
||||||
vol.Optional(ATTR_AUDIO_INPUT): ALSA_CHANNEL,
|
vol.Optional(ATTR_AUDIO_INPUT): ALSA_CHANNEL,
|
||||||
vol.Optional(ATTR_WAIT_BOOT, default=5): WAIT_BOOT,
|
vol.Optional(ATTR_WAIT_BOOT, default=5): WAIT_BOOT,
|
||||||
|
BIN
misc/hassio.png
BIN
misc/hassio.png
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 37 KiB |
@ -1 +1 @@
|
|||||||
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36" version="6.5.6" editor="www.draw.io" type="device"><diagram name="Page-1">5Vptc6M2EP41/ng3gHj9mPiSy820c5n6Q3sfsVBsNTJyhYid/voKkABZkOBY+KYtmYnR6pVn99ld1l6A5e74laX77a80Q2ThOdlxAb4sPC8OY/G/Erw2At9xG8GG4awR9QQr/DeSQkdKS5yhQhvIKSUc73UhpHmOINdkKWP0oA97okTfdZ9ukCFYwZSY0t9xxrdS6oZJ1/GA8GYrt469sOlYp/B5w2iZy/0WHniqr6Z7l6q15IMW2zSjh54I3C3AklHKm7vdcYlIBa2CrZl3P9LbnpuhnE+Z4DUTXlJSInXikIipt09UrCAOyF8lKOFfJVUdn4paZTdigNjtKD5ERw206DtIYKrenLJdSrrJ4m5TfX5fqX3E2Zqtmg4JS7urd9hijlb7FFbtg7A2MWjLd0S03Oo0mJAlJZTVowXYKIRQyAvO6DPq9Tj1Jc+/kutLvF4Q4+g4CqHbKkbYO6I7xNmrGKImJKCZIm09SKRuD53l+Arobc9oQjkulca6aZfuFCZupM6G9QcM/X3LcaW31WvB0e5CNGGG1vF6CE0QggRkrb7sAhhNBNCzAKBvAPiFwmfELkUOokCQ/trI+SZy3hBywAJyoYHcw9JArXaFqJpRUe9MLscQDXN5HQd+4NjB0A8DHcPQxDBwTAgDCxAmBl4oE3FINinjW7qheUruOumtjmgPPXTE/I9K/DkKZPOH6srFwZq+QDV/yBX+RJy/ygiclpwKUbfxL5Tu5RrNUavzvQ20eBxaMihHRTJ4p2yDeM9uTHUwRFKOX/TVLwFX5RK20fXeQDcB3im+deMRMSweALGfBbp/JdCj0Xxi3UX48xIMN6wSjNMEYlXuEXvBhXAJagOm+h7Sovj2fTTBaMXr0aSjMwP3fbdluKflMgybVEN3aFmA4sy347ZAoLstMJB1uPGA33JtRE3Xm4Nbbo9Yyou13NJ4VbuxeUnkqveOHouiK7EIzOO6NHh1dE/iQtc89VyFwIPfVK9YQgCJYBqGSnyPidpzqm5QnpmLCWFvqcFMfrm0qlgvvlZQUm8cvaxJrPLpRjy6wLByU9dxRSmKn6CtLFR3Rd5A/t56HS1/9224ovDKXHE/O3qQ/+zG8aWBfiKtPmjxwLR4d0Sn1i3enyVUSJ30srCJCPYcTk5zpHmb8xQ2Vl+AJXtp+WpPYdeKPa5ZUrjJMpoXhhqLbbqvbveMQlQU73sn3ZVN9lX34qr9fZMTCt07XhiBxANhEHtx7PhgpqRqyJN5bmB6ssSCI1O1nDmJ0rVOHdWlqYAkU59uc7zoXEAAOfWR4vq9Q5WqneE0Wq3Q0FJO6hdSz1ynobKxTm0U7dNMs5PYJCjk1KxYKX6WO9IMALcVOzAUyKdrRB5pgTmmuRiyppzTnRhAqo7btoitVVbrMna3xg3Bm2oup+fRvCvEnpZu5QYWiHxS0wEDNR0wkJBYqciaNJ5AUifSWOq/x1LX5OgUOk5Ity8PgO97LQshEng/L0SqvXsMPBwOpvcmBO+LWg2SiZDQMrs4Tl6FQInuz3xnIKeP5iovgLcLo9K4P5DEn8mRmTLEXqzt3hyaQ3qj0faDNPFNmjTmaz+S+icmc+pN7YVAMP6tjfNQrkcjIUzZ5fQL62uAfkH1Z4d+CThJJ4boN1TdsxLBopnY17f7yGaWOT9lP8i+YAb2TVZjYJDkK+bbuekxFp2QmwUomocevnppvQo94v9LcEpCnaOR5dgU/idjk/m9+G9oX71qUYbReBXl30s+Vf6dgXyi2f0WqlFG93szcPcP</diagram></mxfile>
|
<mxfile userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36" version="7.9.5" editor="www.draw.io" type="device"><diagram name="Page-1" id="535f6c39-9b73-04c2-941c-82630de90f1a">5VrLcqM4FP0aLzsFiOcycefRVTPVqfFippdYKLYmMvII4cd8/QiQDEKQ4Bicnmp7Yevqybk691zJnoH55vDI4u36d5ogMnOs5DADX2eOY1vAER+F5VhZ3DCoDCuGE9moNizwv0j1lNYcJyjTGnJKCcdb3QhpmiLINVvMGN3rzV4o0WfdxitkGBYwJqb1T5zwtbTaflRXPCG8WsupQ8evKpYxfF0xmqdyvpkDXspXVb2J1VjyQbN1nNB9wwTuZ2DOKOXVt81hjkiBrYKt6vfQU3taN0MpH9JB+mkXkxypFftEdL17oWIEsUB+lKD4/+RUVXzJSpfdigZitoP4EBUl0KJuL4EpalPKNjGpO4tvq+Lz+0LNI9ZWTVVVSFhOszr7NeZosY1hUd6L7SYarfmGiJJdrAYTMqeEsrK1ABv5EAp7xhl9RY0aq3zJ9S/k+B14SdMOMY4ODZPE7xHRDeLsKJqo2ghUXeRe9yLp2329c1wF9LqxaXzZLpabdXUaunaY+CJ91u0/YPjvW4oLvy2OGUebC9GECVqGyy40gQ8ikJz8NS6AwUAAnREAdA0Av1L4itilyEHkCdJfGznXRM7pQg6MgJxvIPc0N1ArQyEqehTUO5PLIUTdXF6GnutZ42Do+p6OoW9i6FkmhN4IEEYGXigROiSLlPE1XdE0Jve19U5HtIEeOmD+V2G+CTxZ/KGqUrGwqs5TxR9yhL8R50epwHHOqTDVE/9G6VaO0Qt1RnMG5fKlyvOYrRDXtknxYG+6gyESc7zTBfgScFUuMTa6zhvoRiLxaeFbFp4Rw+IBELsS6O5ngR705hPLWuHPSzBsv0gw2gnEIt8itsOZCAlqAqbqnuIs+/a9N8E4mZe9SUe9Dez3w5YRnuZz369SDT2gJR4KE3ecsAU8PWyBjqzDDjvilj2GatrOFNyyG8RSUezELY1XZRgbSqJMMIPfFqcCYYBEbA4MlfkBE7WKQVyz1WmkQbbgs8gGpolwmhd0J7Tkoy62A9xAzIe6EKWJOZgwNobqTPjn80sc64Sfpl0qHjSSKzHKl1vx6ALDIppdJ2LFKHyBYyWresRyOtL8U3DS0nx3jIjlX5kr9o2l5wI3dhhemg8MpFWDLilNkcaVN9NmjRHAZITal9dnhDuJ4kifNZK5kRAe7tC+awqYs92Jzx922Kdpk2veTHzAgRoIvd4832d9InK52zrx/rjrrqE1pqduk4SmmeGvbB1vi69bRiHKsvd1RhelwarzIF6lcleHAMFSy/EDEDnA90InDC0XTJRFd2mSY3umJkUjSJK6vJsypNWltuRcmtTJsNck2Sgn2/FClez6THF50JQuV2ei9rlJjVDRUnZyGjfnZ45TUdkYp9wUp6cZtk9Ck6CQU/OKUvEz35CqAbgrqIChQD5eIvJMM8wxTUWTJeWcbkQDUlTcnX610K7Sy98t6jFuCV4VfTk9j+b1zXv7rl5OMAKRW5d4oOMSD3SklqNcwZs0HkBSK9BY6r7HUtvk6BA6XkXzztTxQYqofkH8KZIZtZgGA/f7vRm9CcHbrHSDZCIkNE8u1smrECjS45lrdZzOgqnuk8DbN+Fyc3/gOHYmRybK5RtaW58Bq0U6vWo7jCauSRO1WydXUre1ZdrRdDwJBP0/01lP+bJXCWHMLqefX7466OcV73HoF4FWOtFFv67r3FEULJiIfc19H4yZZU5P2WHs867BvsFu9AySPGK+npoefeqE7MRDwTT0cNWh9Sr0CH8VcYp8naPBZdrk/xraZP4R4g+0LY5alGHUf4vy/yWfusifgHyiWP/5rXJG/Q9DcP8f</diagram></mxfile>
|
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
@ -1 +0,0 @@
|
|||||||
<mxfile userAgent="Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:53.0) Gecko/20100101 Firefox/53.0" version="6.5.8" editor="www.draw.io" type="device"><diagram name="Page-1">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</diagram></mxfile>
|
|
2
setup.py
2
setup.py
@ -46,8 +46,6 @@ setup(
|
|||||||
'colorlog',
|
'colorlog',
|
||||||
'voluptuous',
|
'voluptuous',
|
||||||
'gitpython',
|
'gitpython',
|
||||||
'pyotp',
|
|
||||||
'pyqrcode',
|
|
||||||
'pytz',
|
'pytz',
|
||||||
'pyudev'
|
'pyudev'
|
||||||
]
|
]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"hassio": "0.81",
|
"hassio": "0.82",
|
||||||
"homeassistant": "0.61",
|
"homeassistant": "0.61",
|
||||||
"resinos": "1.1",
|
"resinos": "1.1",
|
||||||
"resinhup": "0.3",
|
"resinhup": "0.3",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user