mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-27 02:56:31 +00:00
commit
ccff0f5b9e
@ -7,3 +7,7 @@
|
|||||||
|
|
||||||
# Temporary files
|
# Temporary files
|
||||||
**/__pycache__
|
**/__pycache__
|
||||||
|
|
||||||
|
# virtualenv
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
2
API.md
2
API.md
@ -472,6 +472,7 @@ Get all available addons.
|
|||||||
"options": "{}",
|
"options": "{}",
|
||||||
"network": "{}|null",
|
"network": "{}|null",
|
||||||
"host_network": "bool",
|
"host_network": "bool",
|
||||||
|
"host_pid": "bool",
|
||||||
"host_ipc": "bool",
|
"host_ipc": "bool",
|
||||||
"host_dbus": "bool",
|
"host_dbus": "bool",
|
||||||
"privileged": ["NET_ADMIN", "SYS_ADMIN"],
|
"privileged": ["NET_ADMIN", "SYS_ADMIN"],
|
||||||
@ -482,6 +483,7 @@ Get all available addons.
|
|||||||
"logo": "bool",
|
"logo": "bool",
|
||||||
"changelog": "bool",
|
"changelog": "bool",
|
||||||
"hassio_api": "bool",
|
"hassio_api": "bool",
|
||||||
|
"hassio_role": "default|homeassistant|manager|admin",
|
||||||
"homeassistant_api": "bool",
|
"homeassistant_api": "bool",
|
||||||
"full_access": "bool",
|
"full_access": "bool",
|
||||||
"protected": "bool",
|
"protected": "bool",
|
||||||
|
@ -1 +1 @@
|
|||||||
"""Init file for HassIO."""
|
"""Init file for Hass.io."""
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Main file for HassIO."""
|
"""Main file for Hass.io."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
import logging
|
import logging
|
||||||
@ -31,7 +31,7 @@ if __name__ == "__main__":
|
|||||||
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
|
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
|
||||||
loop.set_default_executor(executor)
|
loop.set_default_executor(executor)
|
||||||
|
|
||||||
_LOGGER.info("Initialize Hassio setup")
|
_LOGGER.info("Initialize Hass.io setup")
|
||||||
coresys = bootstrap.initialize_coresys(loop)
|
coresys = bootstrap.initialize_coresys(loop)
|
||||||
|
|
||||||
bootstrap.migrate_system_env(coresys)
|
bootstrap.migrate_system_env(coresys)
|
||||||
@ -43,13 +43,13 @@ if __name__ == "__main__":
|
|||||||
loop.call_soon_threadsafe(bootstrap.reg_signal, loop)
|
loop.call_soon_threadsafe(bootstrap.reg_signal, loop)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_LOGGER.info("Run HassIO")
|
_LOGGER.info("Run Hass.io")
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
finally:
|
finally:
|
||||||
_LOGGER.info("Stopping HassIO")
|
_LOGGER.info("Stopping Hass.io")
|
||||||
loop.run_until_complete(coresys.core.stop())
|
loop.run_until_complete(coresys.core.stop())
|
||||||
executor.shutdown(wait=False)
|
executor.shutdown(wait=False)
|
||||||
loop.close()
|
loop.close()
|
||||||
|
|
||||||
_LOGGER.info("Close Hassio")
|
_LOGGER.info("Close Hass.io")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -26,7 +26,7 @@ from ..const import (
|
|||||||
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_HOST_IPC,
|
ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY, ATTR_HOST_IPC,
|
||||||
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_DISCOVERY, ATTR_SERVICES,
|
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_DISCOVERY, ATTR_SERVICES,
|
||||||
ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API, ATTR_FULL_ACCESS,
|
ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API, ATTR_FULL_ACCESS,
|
||||||
ATTR_PROTECTED, ATTR_ACCESS_TOKEN,
|
ATTR_PROTECTED, ATTR_ACCESS_TOKEN, ATTR_HOST_PID, ATTR_HASSIO_ROLE,
|
||||||
SECURITY_PROFILE, SECURITY_DISABLE, SECURITY_DEFAULT)
|
SECURITY_PROFILE, SECURITY_DISABLE, SECURITY_DEFAULT)
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
from ..docker.addon import DockerAddon
|
from ..docker.addon import DockerAddon
|
||||||
@ -307,6 +307,11 @@ class Addon(CoreSysAttributes):
|
|||||||
"""Return True if addon run on host network."""
|
"""Return True if addon run on host network."""
|
||||||
return self._mesh[ATTR_HOST_NETWORK]
|
return self._mesh[ATTR_HOST_NETWORK]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def host_pid(self):
|
||||||
|
"""Return True if addon run on host PID namespace."""
|
||||||
|
return self._mesh[ATTR_HOST_PID]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def host_ipc(self):
|
def host_ipc(self):
|
||||||
"""Return True if addon run on host IPC namespace."""
|
"""Return True if addon run on host IPC namespace."""
|
||||||
@ -371,6 +376,11 @@ class Addon(CoreSysAttributes):
|
|||||||
"""Return True if the add-on access to Home-Assistant api proxy."""
|
"""Return True if the add-on access to Home-Assistant api proxy."""
|
||||||
return self._mesh[ATTR_HOMEASSISTANT_API]
|
return self._mesh[ATTR_HOMEASSISTANT_API]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hassio_role(self):
|
||||||
|
"""Return Hass.io role for API."""
|
||||||
|
return self._mesh[ATTR_HASSIO_ROLE]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def with_stdin(self):
|
def with_stdin(self):
|
||||||
"""Return True if the add-on access use stdin input."""
|
"""Return True if the add-on access use stdin input."""
|
||||||
@ -603,7 +613,7 @@ class Addon(CoreSysAttributes):
|
|||||||
return vol.Schema(dict)
|
return vol.Schema(dict)
|
||||||
return vol.Schema(vol.All(dict, validate_options(raw_schema)))
|
return vol.Schema(vol.All(dict, validate_options(raw_schema)))
|
||||||
|
|
||||||
def test_udpate_schema(self):
|
def test_update_schema(self):
|
||||||
"""Check if the exists config valid after update."""
|
"""Check if the exists config valid after update."""
|
||||||
if not self.is_installed or self.is_detached:
|
if not self.is_installed or self.is_detached:
|
||||||
return True
|
return True
|
||||||
|
@ -6,7 +6,8 @@ import re
|
|||||||
|
|
||||||
from ..const import (
|
from ..const import (
|
||||||
SECURITY_DISABLE, SECURITY_PROFILE, PRIVILEGED_NET_ADMIN,
|
SECURITY_DISABLE, SECURITY_PROFILE, PRIVILEGED_NET_ADMIN,
|
||||||
PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO)
|
PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO, PRIVILEGED_SYS_PTRACE,
|
||||||
|
ROLE_ADMIN, ROLE_MANAGER)
|
||||||
|
|
||||||
RE_SHA1 = re.compile(r"[a-f0-9]{8}")
|
RE_SHA1 = re.compile(r"[a-f0-9]{8}")
|
||||||
|
|
||||||
@ -33,13 +34,23 @@ def rating_security(addon):
|
|||||||
|
|
||||||
# Privileged options
|
# Privileged options
|
||||||
if addon.privileged in (PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN,
|
if addon.privileged in (PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN,
|
||||||
PRIVILEGED_SYS_RAWIO):
|
PRIVILEGED_SYS_RAWIO, PRIVILEGED_SYS_PTRACE):
|
||||||
rating += -1
|
rating += -1
|
||||||
|
|
||||||
|
# API Hass.io role
|
||||||
|
if addon.hassio_role == ROLE_MANAGER:
|
||||||
|
rating += -1
|
||||||
|
elif addon.hassio_role == ROLE_ADMIN:
|
||||||
|
rating += -2
|
||||||
|
|
||||||
# Not secure Networking
|
# Not secure Networking
|
||||||
if addon.host_network:
|
if addon.host_network:
|
||||||
rating += -1
|
rating += -1
|
||||||
|
|
||||||
|
# Insecure PID namespace
|
||||||
|
if addon.host_pid:
|
||||||
|
rating += -2
|
||||||
|
|
||||||
# Full Access
|
# Full Access
|
||||||
if addon.with_full_access:
|
if addon.with_full_access:
|
||||||
rating += -2
|
rating += -2
|
||||||
|
@ -19,10 +19,11 @@ from ..const import (
|
|||||||
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY,
|
ATTR_ARGS, ATTR_GPIO, ATTR_HOMEASSISTANT_API, ATTR_STDIN, ATTR_LEGACY,
|
||||||
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_SERVICES, ATTR_DISCOVERY,
|
ATTR_HOST_DBUS, ATTR_AUTO_UART, ATTR_SERVICES, ATTR_DISCOVERY,
|
||||||
ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API, ATTR_PROTECTED,
|
ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API, ATTR_PROTECTED,
|
||||||
ATTR_FULL_ACCESS, ATTR_ACCESS_TOKEN,
|
ATTR_FULL_ACCESS, ATTR_ACCESS_TOKEN, ATTR_HOST_PID, ATTR_HASSIO_ROLE,
|
||||||
PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO,
|
PRIVILEGED_NET_ADMIN, PRIVILEGED_SYS_ADMIN, PRIVILEGED_SYS_RAWIO,
|
||||||
PRIVILEGED_IPC_LOCK, PRIVILEGED_SYS_TIME, PRIVILEGED_SYS_NICE,
|
PRIVILEGED_IPC_LOCK, PRIVILEGED_SYS_TIME, PRIVILEGED_SYS_NICE,
|
||||||
PRIVILEGED_SYS_RESOURCE)
|
PRIVILEGED_SYS_RESOURCE, PRIVILEGED_SYS_PTRACE,
|
||||||
|
ROLE_DEFAULT, ROLE_HOMEASSISTANT, ROLE_MANAGER, ROLE_ADMIN)
|
||||||
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE
|
from ..validate import NETWORK_PORT, DOCKER_PORTS, ALSA_DEVICE
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -69,6 +70,14 @@ PRIVILEGED_ALL = [
|
|||||||
PRIVILEGED_SYS_TIME,
|
PRIVILEGED_SYS_TIME,
|
||||||
PRIVILEGED_SYS_NICE,
|
PRIVILEGED_SYS_NICE,
|
||||||
PRIVILEGED_SYS_RESOURCE,
|
PRIVILEGED_SYS_RESOURCE,
|
||||||
|
PRIVILEGED_SYS_PTRACE,
|
||||||
|
]
|
||||||
|
|
||||||
|
ROLE_ALL = [
|
||||||
|
ROLE_DEFAULT,
|
||||||
|
ROLE_HOMEASSISTANT,
|
||||||
|
ROLE_MANAGER,
|
||||||
|
ROLE_ADMIN,
|
||||||
]
|
]
|
||||||
|
|
||||||
BASE_IMAGE = {
|
BASE_IMAGE = {
|
||||||
@ -104,6 +113,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
|
|||||||
vol.Optional(ATTR_WEBUI):
|
vol.Optional(ATTR_WEBUI):
|
||||||
vol.Match(r"^(?:https?|\[PROTO:\w+\]):\/\/\[HOST\]:\[PORT:\d+\].*$"),
|
vol.Match(r"^(?:https?|\[PROTO:\w+\]):\/\/\[HOST\]:\[PORT:\d+\].*$"),
|
||||||
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
||||||
|
vol.Optional(ATTR_HOST_PID, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_HOST_IPC, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HOST_IPC, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_HOST_DBUS, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HOST_DBUS, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_DEVICES): [vol.Match(r"^(.*):(.*):([rwm]{1,3})$")],
|
vol.Optional(ATTR_DEVICES): [vol.Match(r"^(.*):(.*):([rwm]{1,3})$")],
|
||||||
@ -119,6 +129,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
|
|||||||
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
|
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
|
||||||
|
vol.Optional(ATTR_HASSIO_ROLE, default=ROLE_DEFAULT): vol.In(ROLE_ALL),
|
||||||
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
|
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_STDIN, default=False): vol.Boolean(),
|
vol.Optional(ATTR_STDIN, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_LEGACY, default=False): vol.Boolean(),
|
vol.Optional(ATTR_LEGACY, default=False): vol.Boolean(),
|
||||||
|
@ -19,9 +19,9 @@ from ..const import (
|
|||||||
ATTR_CPU_PERCENT, ATTR_MEMORY_LIMIT, ATTR_MEMORY_USAGE, ATTR_NETWORK_TX,
|
ATTR_CPU_PERCENT, ATTR_MEMORY_LIMIT, ATTR_MEMORY_USAGE, ATTR_NETWORK_TX,
|
||||||
ATTR_NETWORK_RX, ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_ICON, ATTR_SERVICES,
|
ATTR_NETWORK_RX, ATTR_BLK_READ, ATTR_BLK_WRITE, ATTR_ICON, ATTR_SERVICES,
|
||||||
ATTR_DISCOVERY, ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API,
|
ATTR_DISCOVERY, ATTR_APPARMOR, ATTR_DEVICETREE, ATTR_DOCKER_API,
|
||||||
ATTR_FULL_ACCESS, ATTR_PROTECTED, ATTR_RATING,
|
ATTR_FULL_ACCESS, ATTR_PROTECTED, ATTR_RATING, ATTR_HOST_PID,
|
||||||
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT,
|
ATTR_HASSIO_ROLE,
|
||||||
REQUEST_FROM)
|
CONTENT_TYPE_PNG, CONTENT_TYPE_BINARY, CONTENT_TYPE_TEXT, REQUEST_FROM)
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
from ..validate import DOCKER_PORTS, ALSA_DEVICE
|
from ..validate import DOCKER_PORTS, ALSA_DEVICE
|
||||||
from ..exceptions import APINotSupportedError
|
from ..exceptions import APINotSupportedError
|
||||||
@ -140,6 +140,7 @@ class APIAddons(CoreSysAttributes):
|
|||||||
ATTR_BUILD: addon.need_build,
|
ATTR_BUILD: addon.need_build,
|
||||||
ATTR_NETWORK: addon.ports,
|
ATTR_NETWORK: addon.ports,
|
||||||
ATTR_HOST_NETWORK: addon.host_network,
|
ATTR_HOST_NETWORK: addon.host_network,
|
||||||
|
ATTR_HOST_PID: addon.host_pid,
|
||||||
ATTR_HOST_IPC: addon.host_ipc,
|
ATTR_HOST_IPC: addon.host_ipc,
|
||||||
ATTR_HOST_DBUS: addon.host_dbus,
|
ATTR_HOST_DBUS: addon.host_dbus,
|
||||||
ATTR_PRIVILEGED: addon.privileged,
|
ATTR_PRIVILEGED: addon.privileged,
|
||||||
@ -152,6 +153,7 @@ class APIAddons(CoreSysAttributes):
|
|||||||
ATTR_WEBUI: addon.webui,
|
ATTR_WEBUI: addon.webui,
|
||||||
ATTR_STDIN: addon.with_stdin,
|
ATTR_STDIN: addon.with_stdin,
|
||||||
ATTR_HASSIO_API: addon.access_hassio_api,
|
ATTR_HASSIO_API: addon.access_hassio_api,
|
||||||
|
ATTR_HASSIO_ROLE: addon.hassio_role,
|
||||||
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
|
ATTR_HOMEASSISTANT_API: addon.access_homeassistant_api,
|
||||||
ATTR_GPIO: addon.with_gpio,
|
ATTR_GPIO: addon.with_gpio,
|
||||||
ATTR_DEVICETREE: addon.with_devicetree,
|
ATTR_DEVICETREE: addon.with_devicetree,
|
||||||
@ -196,6 +198,7 @@ class APIAddons(CoreSysAttributes):
|
|||||||
addon = self._extract_addon(request)
|
addon = self._extract_addon(request)
|
||||||
|
|
||||||
# Have Access
|
# Have Access
|
||||||
|
# REMOVE: don't needed anymore
|
||||||
if addon.slug == request[REQUEST_FROM]:
|
if addon.slug == request[REQUEST_FROM]:
|
||||||
_LOGGER.error("Can't self modify his security!")
|
_LOGGER.error("Can't self modify his security!")
|
||||||
raise APINotSupportedError()
|
raise APINotSupportedError()
|
||||||
|
@ -25,7 +25,7 @@ class APIProxy(CoreSysAttributes):
|
|||||||
hassio_token = request.headers.get(HEADER_HA_ACCESS)
|
hassio_token = request.headers.get(HEADER_HA_ACCESS)
|
||||||
addon = self.sys_addons.from_token(hassio_token)
|
addon = self.sys_addons.from_token(hassio_token)
|
||||||
|
|
||||||
# Need removed with 131
|
# REMOVE 132
|
||||||
if not addon:
|
if not addon:
|
||||||
addon = self.sys_addons.from_uuid(hassio_token)
|
addon = self.sys_addons.from_uuid(hassio_token)
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ class APIProxy(CoreSysAttributes):
|
|||||||
response.get('access_token'))
|
response.get('access_token'))
|
||||||
addon = self.sys_addons.from_token(hassio_token)
|
addon = self.sys_addons.from_token(hassio_token)
|
||||||
|
|
||||||
# Need removed with 131
|
# REMOVE 132
|
||||||
if not addon:
|
if not addon:
|
||||||
addon = self.sys_addons.from_uuid(hassio_token)
|
addon = self.sys_addons.from_uuid(hassio_token)
|
||||||
|
|
||||||
|
@ -5,27 +5,61 @@ import re
|
|||||||
from aiohttp.web import middleware
|
from aiohttp.web import middleware
|
||||||
from aiohttp.web_exceptions import HTTPUnauthorized, HTTPForbidden
|
from aiohttp.web_exceptions import HTTPUnauthorized, HTTPForbidden
|
||||||
|
|
||||||
from ..const import HEADER_TOKEN, REQUEST_FROM
|
from ..const import (
|
||||||
|
HEADER_TOKEN, REQUEST_FROM, ROLE_ADMIN, ROLE_DEFAULT, ROLE_HOMEASSISTANT,
|
||||||
|
ROLE_MANAGER)
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Free to call or have own security concepts
|
||||||
NO_SECURITY_CHECK = re.compile(
|
NO_SECURITY_CHECK = re.compile(
|
||||||
r"^(?:"
|
r"^(?:"
|
||||||
r"|/homeassistant/api/.*$"
|
r"|/homeassistant/api/.*"
|
||||||
r"|/homeassistant/websocket$"
|
r"|/homeassistant/websocket"
|
||||||
r"|/supervisor/ping$"
|
r"|/supervisor/ping"
|
||||||
|
r"|/services.*"
|
||||||
r")$"
|
r")$"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Can called by every add-on
|
||||||
ADDONS_API_BYPASS = re.compile(
|
ADDONS_API_BYPASS = re.compile(
|
||||||
r"^(?:"
|
r"^(?:"
|
||||||
r"|/homeassistant/info$"
|
r"|/homeassistant/info"
|
||||||
r"|/supervisor/info$"
|
r"|/supervisor/info"
|
||||||
r"|/addons(?:/self/[^/]+)?$"
|
r"|/addons(?:/self/(?!security)[^/]+)?"
|
||||||
r")$"
|
r")$"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Policy role add-on API access
|
||||||
|
ADDONS_ROLE_ACCESS = {
|
||||||
|
ROLE_DEFAULT: re.compile(
|
||||||
|
r"^(?:"
|
||||||
|
r"|/[^/]+/info"
|
||||||
|
r")$"
|
||||||
|
),
|
||||||
|
ROLE_HOMEASSISTANT: re.compile(
|
||||||
|
r"^(?:"
|
||||||
|
r"|/homeassistant/.+"
|
||||||
|
r")$"
|
||||||
|
),
|
||||||
|
ROLE_MANAGER: re.compile(
|
||||||
|
r"^(?:"
|
||||||
|
r"|/homeassistant/.+"
|
||||||
|
r"|/host/.+"
|
||||||
|
r"|/hardware/.+"
|
||||||
|
r"|/hassos/.+"
|
||||||
|
r"|/supervisor/.+"
|
||||||
|
r"|/addons/.+/(?!security|options).+"
|
||||||
|
r"|/addons(?:/self/(?!security).+)"
|
||||||
|
r"|/snapshots.*"
|
||||||
|
r")$"
|
||||||
|
),
|
||||||
|
ROLE_ADMIN: re.compile(
|
||||||
|
r".+"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class SecurityMiddleware(CoreSysAttributes):
|
class SecurityMiddleware(CoreSysAttributes):
|
||||||
"""Security middleware functions."""
|
"""Security middleware functions."""
|
||||||
@ -66,17 +100,22 @@ class SecurityMiddleware(CoreSysAttributes):
|
|||||||
addon = None
|
addon = None
|
||||||
if hassio_token and not request_from:
|
if hassio_token and not request_from:
|
||||||
addon = self.sys_addons.from_token(hassio_token)
|
addon = self.sys_addons.from_token(hassio_token)
|
||||||
# Need removed with 131
|
# REMOVE 132
|
||||||
if not addon:
|
if not addon:
|
||||||
addon = self.sys_addons.from_uuid(hassio_token)
|
addon = self.sys_addons.from_uuid(hassio_token)
|
||||||
|
|
||||||
# Check Add-on API access
|
# Check Add-on API access
|
||||||
if addon and addon.access_hassio_api:
|
if addon and ADDONS_API_BYPASS.match(request.path):
|
||||||
_LOGGER.info("%s access from %s", request.path, addon.slug)
|
|
||||||
request_from = addon.slug
|
|
||||||
elif addon and ADDONS_API_BYPASS.match(request.path):
|
|
||||||
_LOGGER.debug("Passthrough %s from %s", request.path, addon.slug)
|
_LOGGER.debug("Passthrough %s from %s", request.path, addon.slug)
|
||||||
request_from = addon.slug
|
request_from = addon.slug
|
||||||
|
elif addon and addon.access_hassio_api:
|
||||||
|
# Check Role
|
||||||
|
if ADDONS_ROLE_ACCESS[addon.hassio_role].match(request.path):
|
||||||
|
_LOGGER.info("%s access from %s", request.path, addon.slug)
|
||||||
|
request_from = addon.slug
|
||||||
|
else:
|
||||||
|
_LOGGER.warning("%s no role for %s", request.path, addon.slug)
|
||||||
|
request_from = addon.slug # REMOVE: 132
|
||||||
|
|
||||||
if request_from:
|
if request_from:
|
||||||
request[REQUEST_FROM] = request_from
|
request[REQUEST_FROM] = request_from
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Bootstrap HassIO."""
|
"""Bootstrap Hass.io."""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
@ -62,55 +62,55 @@ def initialize_coresys(loop):
|
|||||||
|
|
||||||
|
|
||||||
def initialize_system_data(coresys):
|
def initialize_system_data(coresys):
|
||||||
"""Setup default config and create folders."""
|
"""Set up the default configuration and create folders."""
|
||||||
config = coresys.config
|
config = coresys.config
|
||||||
|
|
||||||
# homeassistant config folder
|
# Home Assistant configuration folder
|
||||||
if not config.path_homeassistant.is_dir():
|
if not config.path_homeassistant.is_dir():
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Create Home-Assistant config folder %s",
|
"Create Home Assistant configuration folder %s",
|
||||||
config.path_homeassistant)
|
config.path_homeassistant)
|
||||||
config.path_homeassistant.mkdir()
|
config.path_homeassistant.mkdir()
|
||||||
|
|
||||||
# hassio ssl folder
|
# hassio ssl folder
|
||||||
if not config.path_ssl.is_dir():
|
if not config.path_ssl.is_dir():
|
||||||
_LOGGER.info("Create hassio ssl folder %s", config.path_ssl)
|
_LOGGER.info("Create Hass.io SSL/TLS folder %s", config.path_ssl)
|
||||||
config.path_ssl.mkdir()
|
config.path_ssl.mkdir()
|
||||||
|
|
||||||
# hassio addon data folder
|
# hassio addon data folder
|
||||||
if not config.path_addons_data.is_dir():
|
if not config.path_addons_data.is_dir():
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"Create hassio addon data folder %s", config.path_addons_data)
|
"Create Hass.io Add-on data folder %s", config.path_addons_data)
|
||||||
config.path_addons_data.mkdir(parents=True)
|
config.path_addons_data.mkdir(parents=True)
|
||||||
|
|
||||||
if not config.path_addons_local.is_dir():
|
if not config.path_addons_local.is_dir():
|
||||||
_LOGGER.info("Create hassio addon local repository folder %s",
|
_LOGGER.info("Create Hass.io Add-on local repository folder %s",
|
||||||
config.path_addons_local)
|
config.path_addons_local)
|
||||||
config.path_addons_local.mkdir(parents=True)
|
config.path_addons_local.mkdir(parents=True)
|
||||||
|
|
||||||
if not config.path_addons_git.is_dir():
|
if not config.path_addons_git.is_dir():
|
||||||
_LOGGER.info("Create hassio addon git repositories folder %s",
|
_LOGGER.info("Create Hass.io Add-on git repositories folder %s",
|
||||||
config.path_addons_git)
|
config.path_addons_git)
|
||||||
config.path_addons_git.mkdir(parents=True)
|
config.path_addons_git.mkdir(parents=True)
|
||||||
|
|
||||||
# hassio tmp folder
|
# hassio tmp folder
|
||||||
if not config.path_tmp.is_dir():
|
if not config.path_tmp.is_dir():
|
||||||
_LOGGER.info("Create hassio temp folder %s", config.path_tmp)
|
_LOGGER.info("Create Hass.io temp folder %s", config.path_tmp)
|
||||||
config.path_tmp.mkdir(parents=True)
|
config.path_tmp.mkdir(parents=True)
|
||||||
|
|
||||||
# hassio backup folder
|
# hassio backup folder
|
||||||
if not config.path_backup.is_dir():
|
if not config.path_backup.is_dir():
|
||||||
_LOGGER.info("Create hassio backup folder %s", config.path_backup)
|
_LOGGER.info("Create Hass.io backup folder %s", config.path_backup)
|
||||||
config.path_backup.mkdir()
|
config.path_backup.mkdir()
|
||||||
|
|
||||||
# share folder
|
# share folder
|
||||||
if not config.path_share.is_dir():
|
if not config.path_share.is_dir():
|
||||||
_LOGGER.info("Create hassio share folder %s", config.path_share)
|
_LOGGER.info("Create Hass.io share folder %s", config.path_share)
|
||||||
config.path_share.mkdir()
|
config.path_share.mkdir()
|
||||||
|
|
||||||
# apparmor folder
|
# apparmor folder
|
||||||
if not config.path_apparmor.is_dir():
|
if not config.path_apparmor.is_dir():
|
||||||
_LOGGER.info("Create hassio apparmor folder %s", config.path_apparmor)
|
_LOGGER.info("Create Hass.io Apparmor folder %s", config.path_apparmor)
|
||||||
config.path_apparmor.mkdir()
|
config.path_apparmor.mkdir()
|
||||||
|
|
||||||
return config
|
return config
|
||||||
@ -126,7 +126,7 @@ def migrate_system_env(coresys):
|
|||||||
try:
|
try:
|
||||||
old_build.rmdir()
|
old_build.rmdir()
|
||||||
except OSError:
|
except OSError:
|
||||||
_LOGGER.warning("Can't cleanup old addons build dir.")
|
_LOGGER.warning("Can't cleanup old Add-on build directory")
|
||||||
|
|
||||||
|
|
||||||
def initialize_logging():
|
def initialize_logging():
|
||||||
@ -166,24 +166,24 @@ def check_environment():
|
|||||||
|
|
||||||
# check docker socket
|
# check docker socket
|
||||||
if not SOCKET_DOCKER.is_socket():
|
if not SOCKET_DOCKER.is_socket():
|
||||||
_LOGGER.fatal("Can't find docker socket!")
|
_LOGGER.fatal("Can't find Docker socket!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# check socat exec
|
# check socat exec
|
||||||
if not shutil.which('socat'):
|
if not shutil.which('socat'):
|
||||||
_LOGGER.fatal("Can't find socat program!")
|
_LOGGER.fatal("Can't find socat!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# check socat exec
|
# check socat exec
|
||||||
if not shutil.which('gdbus'):
|
if not shutil.which('gdbus'):
|
||||||
_LOGGER.fatal("Can't find gdbus program!")
|
_LOGGER.fatal("Can't find gdbus!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def reg_signal(loop):
|
def reg_signal(loop):
|
||||||
"""Register SIGTERM, SIGKILL to stop system."""
|
"""Register SIGTERM and SIGKILL to stop system."""
|
||||||
try:
|
try:
|
||||||
loop.add_signal_handler(
|
loop.add_signal_handler(
|
||||||
signal.SIGTERM, lambda: loop.call_soon(loop.stop))
|
signal.SIGTERM, lambda: loop.call_soon(loop.stop))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Bootstrap HassIO."""
|
"""Bootstrap Hass.io."""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -16,7 +16,7 @@ from .validate import SCHEMA_HASSIO_CONFIG
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
HOMEASSISTANT_CONFIG = PurePath("homeassistant")
|
HOMEASSISTANT_CONFIG = PurePath('homeassistant')
|
||||||
|
|
||||||
HASSIO_SSL = PurePath("ssl")
|
HASSIO_SSL = PurePath("ssl")
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ class CoreConfig(JsonConfig):
|
|||||||
timezone = data.group('timezone')
|
timezone = data.group('timezone')
|
||||||
pytz.timezone(timezone)
|
pytz.timezone(timezone)
|
||||||
except (pytz.exceptions.UnknownTimeZoneError, OSError, AssertionError):
|
except (pytz.exceptions.UnknownTimeZoneError, OSError, AssertionError):
|
||||||
_LOGGER.debug("Can't parse HomeAssistant timezone")
|
_LOGGER.debug("Can't parse Home Assistant timezone")
|
||||||
return self._data[ATTR_TIMEZONE]
|
return self._data[ATTR_TIMEZONE]
|
||||||
|
|
||||||
return timezone
|
return timezone
|
||||||
@ -93,17 +93,17 @@ class CoreConfig(JsonConfig):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_hassio(self):
|
def path_hassio(self):
|
||||||
"""Return hassio data path."""
|
"""Return Hass.io data path."""
|
||||||
return HASSIO_DATA
|
return HASSIO_DATA
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_hassio(self):
|
def path_extern_hassio(self):
|
||||||
"""Return hassio data path extern for docker."""
|
"""Return Hass.io data path external for Docker."""
|
||||||
return PurePath(os.environ['SUPERVISOR_SHARE'])
|
return PurePath(os.environ['SUPERVISOR_SHARE'])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_homeassistant(self):
|
def path_extern_homeassistant(self):
|
||||||
"""Return config path extern for docker."""
|
"""Return config path external for Docker."""
|
||||||
return str(PurePath(self.path_extern_hassio, HOMEASSISTANT_CONFIG))
|
return str(PurePath(self.path_extern_hassio, HOMEASSISTANT_CONFIG))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -113,7 +113,7 @@ class CoreConfig(JsonConfig):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_ssl(self):
|
def path_extern_ssl(self):
|
||||||
"""Return SSL path extern for docker."""
|
"""Return SSL path external for Docker."""
|
||||||
return str(PurePath(self.path_extern_hassio, HASSIO_SSL))
|
return str(PurePath(self.path_extern_hassio, HASSIO_SSL))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -123,42 +123,42 @@ class CoreConfig(JsonConfig):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_addons_core(self):
|
def path_addons_core(self):
|
||||||
"""Return git path for core addons."""
|
"""Return git path for core Add-ons."""
|
||||||
return Path(HASSIO_DATA, ADDONS_CORE)
|
return Path(HASSIO_DATA, ADDONS_CORE)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_addons_git(self):
|
def path_addons_git(self):
|
||||||
"""Return path for git addons."""
|
"""Return path for Git Add-on."""
|
||||||
return Path(HASSIO_DATA, ADDONS_GIT)
|
return Path(HASSIO_DATA, ADDONS_GIT)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_addons_local(self):
|
def path_addons_local(self):
|
||||||
"""Return path for customs addons."""
|
"""Return path for custom Add-ons."""
|
||||||
return Path(HASSIO_DATA, ADDONS_LOCAL)
|
return Path(HASSIO_DATA, ADDONS_LOCAL)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_addons_local(self):
|
def path_extern_addons_local(self):
|
||||||
"""Return path for customs addons."""
|
"""Return path for custom Add-ons."""
|
||||||
return PurePath(self.path_extern_hassio, ADDONS_LOCAL)
|
return PurePath(self.path_extern_hassio, ADDONS_LOCAL)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_addons_data(self):
|
def path_addons_data(self):
|
||||||
"""Return root addon data folder."""
|
"""Return root Add-on data folder."""
|
||||||
return Path(HASSIO_DATA, ADDONS_DATA)
|
return Path(HASSIO_DATA, ADDONS_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_addons_data(self):
|
def path_extern_addons_data(self):
|
||||||
"""Return root addon data folder extern for docker."""
|
"""Return root add-on data folder external for Docker."""
|
||||||
return PurePath(self.path_extern_hassio, ADDONS_DATA)
|
return PurePath(self.path_extern_hassio, ADDONS_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_tmp(self):
|
def path_tmp(self):
|
||||||
"""Return hass.io temp folder."""
|
"""Return Hass.io temp folder."""
|
||||||
return Path(HASSIO_DATA, TMP_DATA)
|
return Path(HASSIO_DATA, TMP_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_tmp(self):
|
def path_extern_tmp(self):
|
||||||
"""Return hass.io temp folder for docker."""
|
"""Return Hass.io temp folder for Docker."""
|
||||||
return PurePath(self.path_extern_hassio, TMP_DATA)
|
return PurePath(self.path_extern_hassio, TMP_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -168,7 +168,7 @@ class CoreConfig(JsonConfig):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_backup(self):
|
def path_extern_backup(self):
|
||||||
"""Return root backup data folder extern for docker."""
|
"""Return root backup data folder external for Docker."""
|
||||||
return PurePath(self.path_extern_hassio, BACKUP_DATA)
|
return PurePath(self.path_extern_hassio, BACKUP_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -178,17 +178,17 @@ class CoreConfig(JsonConfig):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path_apparmor(self):
|
def path_apparmor(self):
|
||||||
"""Return root apparmor profile folder."""
|
"""Return root Apparmor profile folder."""
|
||||||
return Path(HASSIO_DATA, APPARMOR_DATA)
|
return Path(HASSIO_DATA, APPARMOR_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path_extern_share(self):
|
def path_extern_share(self):
|
||||||
"""Return root share data folder extern for docker."""
|
"""Return root share data folder external for Docker."""
|
||||||
return PurePath(self.path_extern_hassio, SHARE_DATA)
|
return PurePath(self.path_extern_hassio, SHARE_DATA)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def addons_repositories(self):
|
def addons_repositories(self):
|
||||||
"""Return list of addons custom repositories."""
|
"""Return list of custom Add-on repositories."""
|
||||||
return self._data[ATTR_ADDONS_CUSTOM_LIST]
|
return self._data[ATTR_ADDONS_CUSTOM_LIST]
|
||||||
|
|
||||||
def add_addon_repository(self, repo):
|
def add_addon_repository(self, repo):
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"""Const file for HassIO."""
|
"""Constants file for Hass.io."""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from ipaddress import ip_network
|
from ipaddress import ip_network
|
||||||
|
|
||||||
HASSIO_VERSION = '130'
|
HASSIO_VERSION = '131'
|
||||||
|
|
||||||
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
||||||
URL_HASSIO_VERSION = \
|
URL_HASSIO_VERSION = \
|
||||||
@ -114,6 +114,7 @@ ATTR_BUILD = 'build'
|
|||||||
ATTR_DEVICES = 'devices'
|
ATTR_DEVICES = 'devices'
|
||||||
ATTR_ENVIRONMENT = 'environment'
|
ATTR_ENVIRONMENT = 'environment'
|
||||||
ATTR_HOST_NETWORK = 'host_network'
|
ATTR_HOST_NETWORK = 'host_network'
|
||||||
|
ATTR_HOST_PID = 'host_pid'
|
||||||
ATTR_HOST_IPC = 'host_ipc'
|
ATTR_HOST_IPC = 'host_ipc'
|
||||||
ATTR_HOST_DBUS = 'host_dbus'
|
ATTR_HOST_DBUS = 'host_dbus'
|
||||||
ATTR_NETWORK = 'network'
|
ATTR_NETWORK = 'network'
|
||||||
@ -183,6 +184,7 @@ ATTR_DOCKER_API = 'docker_api'
|
|||||||
ATTR_FULL_ACCESS = 'full_access'
|
ATTR_FULL_ACCESS = 'full_access'
|
||||||
ATTR_PROTECTED = 'protected'
|
ATTR_PROTECTED = 'protected'
|
||||||
ATTR_RATING = 'rating'
|
ATTR_RATING = 'rating'
|
||||||
|
ATTR_HASSIO_ROLE = 'hassio_role'
|
||||||
|
|
||||||
SERVICE_MQTT = 'mqtt'
|
SERVICE_MQTT = 'mqtt'
|
||||||
|
|
||||||
@ -238,9 +240,15 @@ PRIVILEGED_IPC_LOCK = 'IPC_LOCK'
|
|||||||
PRIVILEGED_SYS_TIME = 'SYS_TIME'
|
PRIVILEGED_SYS_TIME = 'SYS_TIME'
|
||||||
PRIVILEGED_SYS_NICE = 'SYS_NICE'
|
PRIVILEGED_SYS_NICE = 'SYS_NICE'
|
||||||
PRIVILEGED_SYS_RESOURCE = 'SYS_RESOURCE'
|
PRIVILEGED_SYS_RESOURCE = 'SYS_RESOURCE'
|
||||||
|
PRIVILEGED_SYS_PTRACE = 'SYS_PTRACE'
|
||||||
|
|
||||||
FEATURES_SHUTDOWN = 'shutdown'
|
FEATURES_SHUTDOWN = 'shutdown'
|
||||||
FEATURES_REBOOT = 'reboot'
|
FEATURES_REBOOT = 'reboot'
|
||||||
FEATURES_HASSOS = 'hassos'
|
FEATURES_HASSOS = 'hassos'
|
||||||
FEATURES_HOSTNAME = 'hostname'
|
FEATURES_HOSTNAME = 'hostname'
|
||||||
FEATURES_SERVICES = 'services'
|
FEATURES_SERVICES = 'services'
|
||||||
|
|
||||||
|
ROLE_DEFAULT = 'default'
|
||||||
|
ROLE_HOMEASSISTANT = 'homeassistant'
|
||||||
|
ROLE_MANAGER = 'manager'
|
||||||
|
ROLE_ADMIN = 'admin'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Main file for HassIO."""
|
"""Main file for Hass.io."""
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
@ -14,10 +14,10 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class HassIO(CoreSysAttributes):
|
class HassIO(CoreSysAttributes):
|
||||||
"""Main object of hassio."""
|
"""Main object of Hass.io."""
|
||||||
|
|
||||||
def __init__(self, coresys):
|
def __init__(self, coresys):
|
||||||
"""Initialize hassio object."""
|
"""Initialize Hass.io object."""
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
|
|
||||||
async def setup(self):
|
async def setup(self):
|
||||||
@ -56,7 +56,7 @@ class HassIO(CoreSysAttributes):
|
|||||||
self.sys_create_task(self.sys_dns.start())
|
self.sys_create_task(self.sys_dns.start())
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""Start HassIO orchestration."""
|
"""Start Hass.io orchestration."""
|
||||||
# on release channel, try update itself
|
# on release channel, try update itself
|
||||||
# on dev mode, only read new versions
|
# on dev mode, only read new versions
|
||||||
if not self.sys_dev and self.sys_supervisor.need_update:
|
if not self.sys_dev and self.sys_supervisor.need_update:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"""Handle core shared data."""
|
"""Handle core shared data."""
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from .const import CHANNEL_DEV
|
from .const import CHANNEL_DEV
|
||||||
@ -49,21 +48,21 @@ class CoreSys:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def arch(self):
|
def arch(self):
|
||||||
"""Return running arch of hass.io system."""
|
"""Return running arch of the Hass.io system."""
|
||||||
if self._supervisor:
|
if self._supervisor:
|
||||||
return self._supervisor.arch
|
return self._supervisor.arch
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def machine(self):
|
def machine(self):
|
||||||
"""Return running machine type of hass.io system."""
|
"""Return running machine type of the Hass.io system."""
|
||||||
if self._homeassistant:
|
if self._homeassistant:
|
||||||
return self._homeassistant.machine
|
return self._homeassistant.machine
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dev(self):
|
def dev(self):
|
||||||
"""Return True if we run dev modus."""
|
"""Return True if we run dev mode."""
|
||||||
return self._updater.channel == CHANNEL_DEV
|
return self._updater.channel == CHANNEL_DEV
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -118,21 +117,21 @@ class CoreSys:
|
|||||||
|
|
||||||
@core.setter
|
@core.setter
|
||||||
def core(self, value):
|
def core(self, value):
|
||||||
"""Set a HassIO object."""
|
"""Set a Hass.io object."""
|
||||||
if self._core:
|
if self._core:
|
||||||
raise RuntimeError("HassIO already set!")
|
raise RuntimeError("Hass.io already set!")
|
||||||
self._core = value
|
self._core = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def homeassistant(self):
|
def homeassistant(self):
|
||||||
"""Return HomeAssistant object."""
|
"""Return Home Assistant object."""
|
||||||
return self._homeassistant
|
return self._homeassistant
|
||||||
|
|
||||||
@homeassistant.setter
|
@homeassistant.setter
|
||||||
def homeassistant(self, value):
|
def homeassistant(self, value):
|
||||||
"""Set a HomeAssistant object."""
|
"""Set a HomeAssistant object."""
|
||||||
if self._homeassistant:
|
if self._homeassistant:
|
||||||
raise RuntimeError("HomeAssistant already set!")
|
raise RuntimeError("Home Assistant already set!")
|
||||||
self._homeassistant = value
|
self._homeassistant = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""DBus interface objects."""
|
"""D-Bus interface objects."""
|
||||||
|
|
||||||
from .systemd import Systemd
|
from .systemd import Systemd
|
||||||
from .hostname import Hostname
|
from .hostname import Hostname
|
||||||
@ -7,10 +7,10 @@ from ..coresys import CoreSysAttributes
|
|||||||
|
|
||||||
|
|
||||||
class DBusManager(CoreSysAttributes):
|
class DBusManager(CoreSysAttributes):
|
||||||
"""DBus Interface handler."""
|
"""A DBus Interface handler."""
|
||||||
|
|
||||||
def __init__(self, coresys):
|
def __init__(self, coresys):
|
||||||
"""Initialize DBus Interface."""
|
"""Initialize D-Bus interface."""
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
|
|
||||||
self._systemd = Systemd()
|
self._systemd = Systemd()
|
||||||
@ -19,21 +19,21 @@ class DBusManager(CoreSysAttributes):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def systemd(self):
|
def systemd(self):
|
||||||
"""Return Systemd Interface."""
|
"""Return the systemd interface."""
|
||||||
return self._systemd
|
return self._systemd
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hostname(self):
|
def hostname(self):
|
||||||
"""Return hostname Interface."""
|
"""Return the hostname interface."""
|
||||||
return self._hostname
|
return self._hostname
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rauc(self):
|
def rauc(self):
|
||||||
"""Return rauc Interface."""
|
"""Return the rauc interface."""
|
||||||
return self._rauc
|
return self._rauc
|
||||||
|
|
||||||
async def load(self):
|
async def load(self):
|
||||||
"""Connect interfaces to dbus."""
|
"""Connect interfaces to D-Bus."""
|
||||||
await self.systemd.connect()
|
await self.systemd.connect()
|
||||||
await self.hostname.connect()
|
await self.hostname.connect()
|
||||||
await self.rauc.connect()
|
await self.rauc.connect()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""DBus interface for hostname."""
|
"""D-Bus interface for hostname."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .interface import DBusInterface
|
from .interface import DBusInterface
|
||||||
@ -13,10 +13,10 @@ DBUS_OBJECT = '/org/freedesktop/hostname1'
|
|||||||
|
|
||||||
|
|
||||||
class Hostname(DBusInterface):
|
class Hostname(DBusInterface):
|
||||||
"""Handle DBus interface for hostname/system."""
|
"""Handle D-Bus interface for hostname/system."""
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
"""Connect do bus."""
|
"""Connect to system's D-Bus."""
|
||||||
try:
|
try:
|
||||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||||
except DBusError:
|
except DBusError:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"""Interface class for dbus wrappers."""
|
"""Interface class for D-Bus wrappers."""
|
||||||
|
|
||||||
|
|
||||||
class DBusInterface:
|
class DBusInterface:
|
||||||
"""Handle DBus interface for hostname/system."""
|
"""Handle D-Bus interface for hostname/system."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize systemd."""
|
"""Initialize systemd."""
|
||||||
@ -10,9 +10,9 @@ class DBusInterface:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
"""Return True, if they is connected to dbus."""
|
"""Return True, if they is connected to D-Bus."""
|
||||||
return self.dbus is not None
|
return self.dbus is not None
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
"""Connect do bus."""
|
"""Connect to D-Bus."""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""DBus interface for rauc."""
|
"""D-Bus interface for rauc."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .interface import DBusInterface
|
from .interface import DBusInterface
|
||||||
@ -13,10 +13,10 @@ DBUS_OBJECT = '/'
|
|||||||
|
|
||||||
|
|
||||||
class Rauc(DBusInterface):
|
class Rauc(DBusInterface):
|
||||||
"""Handle DBus interface for rauc."""
|
"""Handle D-Bus interface for rauc."""
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
"""Connect do bus."""
|
"""Connect to D-Bus."""
|
||||||
try:
|
try:
|
||||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||||
except DBusError:
|
except DBusError:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Interface to Systemd over dbus."""
|
"""Interface to Systemd over D-Bus."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .interface import DBusInterface
|
from .interface import DBusInterface
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
"""Utils for dbus."""
|
"""Utils for D-Bus."""
|
||||||
|
|
||||||
from ..exceptions import DBusNotConnectedError
|
from ..exceptions import DBusNotConnectedError
|
||||||
|
|
||||||
|
|
||||||
def dbus_connected(method):
|
def dbus_connected(method):
|
||||||
"""Wrapper for check if dbus is connected."""
|
"""Wrapper for check if D-Bus is connected."""
|
||||||
def wrap_dbus(api, *args, **kwargs):
|
def wrap_dbus(api, *args, **kwargs):
|
||||||
"""Check if dbus is connected before call a method."""
|
"""Check if D-Bus is connected before call a method."""
|
||||||
if api.dbus is None:
|
if api.dbus is None:
|
||||||
raise DBusNotConnectedError()
|
raise DBusNotConnectedError()
|
||||||
return method(api, *args, **kwargs)
|
return method(api, *args, **kwargs)
|
||||||
|
@ -165,6 +165,13 @@ class DockerAddon(DockerInterface):
|
|||||||
return 'host'
|
return 'host'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pid_mode(self):
|
||||||
|
"""Return PID mode for addon."""
|
||||||
|
if not self.addon.protected and self.addon.host_pid:
|
||||||
|
return 'host'
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volumes(self):
|
def volumes(self):
|
||||||
"""Generate volumes for mappings."""
|
"""Generate volumes for mappings."""
|
||||||
@ -277,6 +284,7 @@ class DockerAddon(DockerInterface):
|
|||||||
ipc_mode=self.ipc,
|
ipc_mode=self.ipc,
|
||||||
stdin_open=self.addon.with_stdin,
|
stdin_open=self.addon.with_stdin,
|
||||||
network_mode=self.network_mode,
|
network_mode=self.network_mode,
|
||||||
|
pid_mode=self.pid_mode,
|
||||||
ports=self.ports,
|
ports=self.ports,
|
||||||
extra_hosts=self.network_mapping,
|
extra_hosts=self.network_mapping,
|
||||||
devices=self.devices,
|
devices=self.devices,
|
||||||
|
@ -32,26 +32,32 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
"""Return name of docker container."""
|
"""Return name of docker container."""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def meta_config(self):
|
||||||
|
"""Return meta data of config for container/image."""
|
||||||
|
if not self._meta:
|
||||||
|
return {}
|
||||||
|
return self._meta.get('Config', {})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def meta_labels(self):
|
||||||
|
"""Return meta data of labels for container/image."""
|
||||||
|
return self.meta_config.get('Labels', {})
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image(self):
|
def image(self):
|
||||||
"""Return name of docker image."""
|
"""Return name of docker image."""
|
||||||
if not self._meta:
|
return self.meta_config.get('Image')
|
||||||
return None
|
|
||||||
return self._meta['Config']['Image']
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
"""Return version of docker image."""
|
"""Return version of docker image."""
|
||||||
if self._meta and LABEL_VERSION in self._meta['Config']['Labels']:
|
return self.meta_labels.get(LABEL_VERSION)
|
||||||
return self._meta['Config']['Labels'][LABEL_VERSION]
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def arch(self):
|
def arch(self):
|
||||||
"""Return arch of docker image."""
|
"""Return arch of docker image."""
|
||||||
if self._meta and LABEL_ARCH in self._meta['Config']['Labels']:
|
return self.meta_labels.get(LABEL_ARCH)
|
||||||
return self._meta['Config']['Labels'][LABEL_ARCH]
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def in_progress(self):
|
def in_progress(self):
|
||||||
|
@ -68,7 +68,7 @@ class HassOS(CoreSysAttributes):
|
|||||||
def _check_host(self):
|
def _check_host(self):
|
||||||
"""Check if HassOS is availabe."""
|
"""Check if HassOS is availabe."""
|
||||||
if not self.available:
|
if not self.available:
|
||||||
_LOGGER.error("No HassOS availabe")
|
_LOGGER.error("No HassOS available")
|
||||||
raise HassOSNotSupportedError()
|
raise HassOSNotSupportedError()
|
||||||
|
|
||||||
async def _download_raucb(self, version):
|
async def _download_raucb(self, version):
|
||||||
@ -97,7 +97,7 @@ class HassOS(CoreSysAttributes):
|
|||||||
_LOGGER.warning("Can't fetch versions from %s: %s", url, err)
|
_LOGGER.warning("Can't fetch versions from %s: %s", url, err)
|
||||||
|
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
_LOGGER.error("Can't write ota file: %s", err)
|
_LOGGER.error("Can't write OTA file: %s", err)
|
||||||
|
|
||||||
raise HassOSUpdateError()
|
raise HassOSUpdateError()
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ class HassOS(CoreSysAttributes):
|
|||||||
"""
|
"""
|
||||||
self._check_host()
|
self._check_host()
|
||||||
|
|
||||||
_LOGGER.info("Sync config from USB on HassOS.")
|
_LOGGER.info("Syncing configuration from USB with HassOS.")
|
||||||
return self.sys_host.services.restart('hassos-config.service')
|
return self.sys_host.services.restart('hassos-config.service')
|
||||||
|
|
||||||
async def update(self, version=None):
|
async def update(self, version=None):
|
||||||
@ -182,5 +182,5 @@ class HassOS(CoreSysAttributes):
|
|||||||
if await self.instance.update(version):
|
if await self.instance.update(version):
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.error("HassOS CLI update fails.")
|
_LOGGER.error("HassOS CLI update fails")
|
||||||
raise HassOSUpdateError()
|
raise HassOSUpdateError()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""HomeAssistant control object."""
|
"""Home Assistant control object."""
|
||||||
import asyncio
|
import asyncio
|
||||||
from contextlib import asynccontextmanager, suppress
|
from contextlib import asynccontextmanager, suppress
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@ -36,10 +36,10 @@ ConfigResult = attr.make_class('ConfigResult', ['valid', 'log'], frozen=True)
|
|||||||
|
|
||||||
|
|
||||||
class HomeAssistant(JsonConfig, CoreSysAttributes):
|
class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||||
"""Hass core object for handle it."""
|
"""Home Assistant core object for handle it."""
|
||||||
|
|
||||||
def __init__(self, coresys):
|
def __init__(self, coresys):
|
||||||
"""Initialize hass object."""
|
"""Initialize Home Assistant object."""
|
||||||
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
|
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
self.instance = DockerHomeAssistant(coresys)
|
self.instance = DockerHomeAssistant(coresys)
|
||||||
@ -50,16 +50,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
self._access_token_expires = None
|
self._access_token_expires = None
|
||||||
|
|
||||||
async def load(self):
|
async def load(self):
|
||||||
"""Prepare HomeAssistant object."""
|
"""Prepare Home Assistant object."""
|
||||||
if await self.instance.attach():
|
if await self.instance.attach():
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.info("No HomeAssistant docker %s found.", self.image)
|
_LOGGER.info("No Home Assistant Docker image %s found.", self.image)
|
||||||
await self.install_landingpage()
|
await self.install_landingpage()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def machine(self):
|
def machine(self):
|
||||||
"""Return System Machines."""
|
"""Return the system machines."""
|
||||||
return self.instance.machine
|
return self.instance.machine
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -69,81 +69,81 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def api_ip(self):
|
def api_ip(self):
|
||||||
"""Return IP of HomeAssistant instance."""
|
"""Return IP of Home Assistant instance."""
|
||||||
return self.sys_docker.network.gateway
|
return self.sys_docker.network.gateway
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_port(self):
|
def api_port(self):
|
||||||
"""Return network port to home-assistant instance."""
|
"""Return network port to Home Assistant instance."""
|
||||||
return self._data[ATTR_PORT]
|
return self._data[ATTR_PORT]
|
||||||
|
|
||||||
@api_port.setter
|
@api_port.setter
|
||||||
def api_port(self, value):
|
def api_port(self, value):
|
||||||
"""Set network port for home-assistant instance."""
|
"""Set network port for Home Assistant instance."""
|
||||||
self._data[ATTR_PORT] = value
|
self._data[ATTR_PORT] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_password(self):
|
def api_password(self):
|
||||||
"""Return password for home-assistant instance."""
|
"""Return password for Home Assistant instance."""
|
||||||
return self._data.get(ATTR_PASSWORD)
|
return self._data.get(ATTR_PASSWORD)
|
||||||
|
|
||||||
@api_password.setter
|
@api_password.setter
|
||||||
def api_password(self, value):
|
def api_password(self, value):
|
||||||
"""Set password for home-assistant instance."""
|
"""Set password for Home Assistant instance."""
|
||||||
self._data[ATTR_PASSWORD] = value
|
self._data[ATTR_PASSWORD] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_ssl(self):
|
def api_ssl(self):
|
||||||
"""Return if we need ssl to home-assistant instance."""
|
"""Return if we need ssl to Home Assistant instance."""
|
||||||
return self._data[ATTR_SSL]
|
return self._data[ATTR_SSL]
|
||||||
|
|
||||||
@api_ssl.setter
|
@api_ssl.setter
|
||||||
def api_ssl(self, value):
|
def api_ssl(self, value):
|
||||||
"""Set SSL for home-assistant instance."""
|
"""Set SSL for Home Assistant instance."""
|
||||||
self._data[ATTR_SSL] = value
|
self._data[ATTR_SSL] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_url(self):
|
def api_url(self):
|
||||||
"""Return API url to Home-Assistant."""
|
"""Return API url to Home Assistant."""
|
||||||
return "{}://{}:{}".format(
|
return "{}://{}:{}".format(
|
||||||
'https' if self.api_ssl else 'http', self.api_ip, self.api_port
|
'https' if self.api_ssl else 'http', self.api_ip, self.api_port
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def watchdog(self):
|
def watchdog(self):
|
||||||
"""Return True if the watchdog should protect Home-Assistant."""
|
"""Return True if the watchdog should protect Home Assistant."""
|
||||||
return self._data[ATTR_WATCHDOG]
|
return self._data[ATTR_WATCHDOG]
|
||||||
|
|
||||||
@watchdog.setter
|
@watchdog.setter
|
||||||
def watchdog(self, value):
|
def watchdog(self, value):
|
||||||
"""Return True if the watchdog should protect Home-Assistant."""
|
"""Return True if the watchdog should protect Home Assistant."""
|
||||||
self._data[ATTR_WATCHDOG] = value
|
self._data[ATTR_WATCHDOG] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def wait_boot(self):
|
def wait_boot(self):
|
||||||
"""Return time to wait for Home-Assistant startup."""
|
"""Return time to wait for Home Assistant startup."""
|
||||||
return self._data[ATTR_WAIT_BOOT]
|
return self._data[ATTR_WAIT_BOOT]
|
||||||
|
|
||||||
@wait_boot.setter
|
@wait_boot.setter
|
||||||
def wait_boot(self, value):
|
def wait_boot(self, value):
|
||||||
"""Set time to wait for Home-Assistant startup."""
|
"""Set time to wait for Home Assistant startup."""
|
||||||
self._data[ATTR_WAIT_BOOT] = value
|
self._data[ATTR_WAIT_BOOT] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
"""Return version of running homeassistant."""
|
"""Return version of running Home Assistant."""
|
||||||
return self.instance.version
|
return self.instance.version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_version(self):
|
def last_version(self):
|
||||||
"""Return last available version of homeassistant."""
|
"""Return last available version of Home Assistant."""
|
||||||
if self.is_custom_image:
|
if self.is_custom_image:
|
||||||
return self._data.get(ATTR_LAST_VERSION)
|
return self._data.get(ATTR_LAST_VERSION)
|
||||||
return self.sys_updater.version_homeassistant
|
return self.sys_updater.version_homeassistant
|
||||||
|
|
||||||
@last_version.setter
|
@last_version.setter
|
||||||
def last_version(self, value):
|
def last_version(self, value):
|
||||||
"""Set last available version of homeassistant."""
|
"""Set last available version of Home Assistant."""
|
||||||
if value:
|
if value:
|
||||||
self._data[ATTR_LAST_VERSION] = value
|
self._data[ATTR_LAST_VERSION] = value
|
||||||
else:
|
else:
|
||||||
@ -151,14 +151,14 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def image(self):
|
def image(self):
|
||||||
"""Return image name of hass containter."""
|
"""Return image name of the Home Assistant container."""
|
||||||
if self._data.get(ATTR_IMAGE):
|
if self._data.get(ATTR_IMAGE):
|
||||||
return self._data[ATTR_IMAGE]
|
return self._data[ATTR_IMAGE]
|
||||||
return os.environ['HOMEASSISTANT_REPOSITORY']
|
return os.environ['HOMEASSISTANT_REPOSITORY']
|
||||||
|
|
||||||
@image.setter
|
@image.setter
|
||||||
def image(self, value):
|
def image(self, value):
|
||||||
"""Set image name of hass containter."""
|
"""Set image name of Home Assistant container."""
|
||||||
if value:
|
if value:
|
||||||
self._data[ATTR_IMAGE] = value
|
self._data[ATTR_IMAGE] = value
|
||||||
else:
|
else:
|
||||||
@ -172,27 +172,27 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def boot(self):
|
def boot(self):
|
||||||
"""Return True if home-assistant boot is enabled."""
|
"""Return True if Home Assistant boot is enabled."""
|
||||||
return self._data[ATTR_BOOT]
|
return self._data[ATTR_BOOT]
|
||||||
|
|
||||||
@boot.setter
|
@boot.setter
|
||||||
def boot(self, value):
|
def boot(self, value):
|
||||||
"""Set home-assistant boot options."""
|
"""Set Home Assistant boot options."""
|
||||||
self._data[ATTR_BOOT] = value
|
self._data[ATTR_BOOT] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def uuid(self):
|
def uuid(self):
|
||||||
"""Return a UUID of this HomeAssistant."""
|
"""Return a UUID of this Home Assistant instance."""
|
||||||
return self._data[ATTR_UUID]
|
return self._data[ATTR_UUID]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hassio_token(self):
|
def hassio_token(self):
|
||||||
"""Return a access token for Hass.io API."""
|
"""Return a access token for the Hass.io API."""
|
||||||
return self._data.get(ATTR_ACCESS_TOKEN)
|
return self._data.get(ATTR_ACCESS_TOKEN)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def refresh_token(self):
|
def refresh_token(self):
|
||||||
"""Return the refresh token to authenticate with HomeAssistant."""
|
"""Return the refresh token to authenticate with Home Assistant."""
|
||||||
return self._data.get(ATTR_REFRESH_TOKEN)
|
return self._data.get(ATTR_REFRESH_TOKEN)
|
||||||
|
|
||||||
@refresh_token.setter
|
@refresh_token.setter
|
||||||
@ -202,7 +202,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@process_lock
|
@process_lock
|
||||||
async def install_landingpage(self):
|
async def install_landingpage(self):
|
||||||
"""Install a landingpage."""
|
"""Install a landing page."""
|
||||||
_LOGGER.info("Setup HomeAssistant landingpage")
|
_LOGGER.info("Setup HomeAssistant landingpage")
|
||||||
while True:
|
while True:
|
||||||
if await self.instance.install('landingpage'):
|
if await self.instance.install('landingpage'):
|
||||||
@ -211,16 +211,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
await asyncio.sleep(60)
|
await asyncio.sleep(60)
|
||||||
|
|
||||||
# Run landingpage after installation
|
# Run landingpage after installation
|
||||||
_LOGGER.info("Start landingpage")
|
_LOGGER.info("Start landing page")
|
||||||
try:
|
try:
|
||||||
await self._start()
|
await self._start()
|
||||||
except HomeAssistantError:
|
except HomeAssistantError:
|
||||||
_LOGGER.warning("Can't start landingpage")
|
_LOGGER.warning("Can't start landing page")
|
||||||
|
|
||||||
@process_lock
|
@process_lock
|
||||||
async def install(self):
|
async def install(self):
|
||||||
"""Install a landingpage."""
|
"""Install a landing page."""
|
||||||
_LOGGER.info("Setup HomeAssistant")
|
_LOGGER.info("Setup Home Assistant")
|
||||||
while True:
|
while True:
|
||||||
# read homeassistant tag and install it
|
# read homeassistant tag and install it
|
||||||
if not self.last_version:
|
if not self.last_version:
|
||||||
@ -229,18 +229,18 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
tag = self.last_version
|
tag = self.last_version
|
||||||
if tag and await self.instance.install(tag):
|
if tag and await self.instance.install(tag):
|
||||||
break
|
break
|
||||||
_LOGGER.warning("Error on install HomeAssistant. Retry in 60sec")
|
_LOGGER.warning("Error on install Home Assistant. Retry in 60sec")
|
||||||
await asyncio.sleep(60)
|
await asyncio.sleep(60)
|
||||||
|
|
||||||
# finishing
|
# finishing
|
||||||
_LOGGER.info("HomeAssistant docker now installed")
|
_LOGGER.info("Home Assistant docker now installed")
|
||||||
try:
|
try:
|
||||||
if not self.boot:
|
if not self.boot:
|
||||||
return
|
return
|
||||||
_LOGGER.info("Start HomeAssistant")
|
_LOGGER.info("Start Home Assistant")
|
||||||
await self._start()
|
await self._start()
|
||||||
except HomeAssistantError:
|
except HomeAssistantError:
|
||||||
_LOGGER.error("Can't start HomeAssistant!")
|
_LOGGER.error("Can't start Home Assistant!")
|
||||||
finally:
|
finally:
|
||||||
await self.instance.cleanup()
|
await self.instance.cleanup()
|
||||||
|
|
||||||
@ -260,13 +260,13 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
async def _update(to_version):
|
async def _update(to_version):
|
||||||
"""Run Home Assistant update."""
|
"""Run Home Assistant update."""
|
||||||
try:
|
try:
|
||||||
_LOGGER.info("Update HomeAssistant to version %s", to_version)
|
_LOGGER.info("Update Home Assistant to version %s", to_version)
|
||||||
if not await self.instance.update(to_version):
|
if not await self.instance.update(to_version):
|
||||||
raise HomeAssistantUpdateError()
|
raise HomeAssistantUpdateError()
|
||||||
finally:
|
finally:
|
||||||
if running:
|
if running:
|
||||||
await self._start()
|
await self._start()
|
||||||
_LOGGER.info("Successfull run HomeAssistant %s", to_version)
|
_LOGGER.info("Successful run Home Assistant %s", to_version)
|
||||||
|
|
||||||
# Update Home Assistant
|
# Update Home Assistant
|
||||||
with suppress(HomeAssistantError):
|
with suppress(HomeAssistantError):
|
||||||
@ -281,9 +281,9 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
raise HomeAssistantUpdateError()
|
raise HomeAssistantUpdateError()
|
||||||
|
|
||||||
async def _start(self):
|
async def _start(self):
|
||||||
"""Start HomeAssistant docker & wait."""
|
"""Start Home Assistant Docker & wait."""
|
||||||
if await self.instance.is_running():
|
if await self.instance.is_running():
|
||||||
_LOGGER.warning("HomeAssistant allready running!")
|
_LOGGER.warning("Home Assistant is already running!")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Create new API token
|
# Create new API token
|
||||||
@ -296,7 +296,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@process_lock
|
@process_lock
|
||||||
def start(self):
|
def start(self):
|
||||||
"""Run HomeAssistant docker.
|
"""Run Home Assistant docker.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
@ -304,7 +304,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@process_lock
|
@process_lock
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop HomeAssistant docker.
|
"""Stop Home Assistant Docker.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
@ -312,7 +312,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@process_lock
|
@process_lock
|
||||||
async def restart(self):
|
async def restart(self):
|
||||||
"""Restart HomeAssistant docker."""
|
"""Restart Home Assistant Docker."""
|
||||||
await self.instance.stop()
|
await self.instance.stop()
|
||||||
await self._start()
|
await self._start()
|
||||||
|
|
||||||
@ -324,21 +324,21 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
return self.instance.logs()
|
return self.instance.logs()
|
||||||
|
|
||||||
def stats(self):
|
def stats(self):
|
||||||
"""Return stats of HomeAssistant.
|
"""Return stats of Home Assistant.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
return self.instance.stats()
|
return self.instance.stats()
|
||||||
|
|
||||||
def is_running(self):
|
def is_running(self):
|
||||||
"""Return True if docker container is running.
|
"""Return True if Docker container is running.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
return self.instance.is_running()
|
return self.instance.is_running()
|
||||||
|
|
||||||
def is_initialize(self):
|
def is_initialize(self):
|
||||||
"""Return True if a docker container is exists.
|
"""Return True if a Docker container is exists.
|
||||||
|
|
||||||
Return a coroutine.
|
Return a coroutine.
|
||||||
"""
|
"""
|
||||||
@ -350,7 +350,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
return self.instance.in_progress or self.lock.locked()
|
return self.instance.in_progress or self.lock.locked()
|
||||||
|
|
||||||
async def check_config(self):
|
async def check_config(self):
|
||||||
"""Run homeassistant config check."""
|
"""Run Home Assistant config check."""
|
||||||
result = await self.instance.execute_command(
|
result = await self.instance.execute_command(
|
||||||
"python3 -m homeassistant -c /config --script check_config"
|
"python3 -m homeassistant -c /config --script check_config"
|
||||||
)
|
)
|
||||||
@ -381,10 +381,10 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
}
|
}
|
||||||
) as resp:
|
) as resp:
|
||||||
if resp.status != 200:
|
if resp.status != 200:
|
||||||
_LOGGER.error("Can't update HomeAssistant access token!")
|
_LOGGER.error("Can't update Home Assistant access token!")
|
||||||
raise HomeAssistantAuthError()
|
raise HomeAssistantAuthError()
|
||||||
|
|
||||||
_LOGGER.info("Updated HomeAssistant API token")
|
_LOGGER.info("Updated Home Assistant API token")
|
||||||
tokens = await resp.json()
|
tokens = await resp.json()
|
||||||
self.access_token = tokens['access_token']
|
self.access_token = tokens['access_token']
|
||||||
self._access_token_expires = \
|
self._access_token_expires = \
|
||||||
@ -429,14 +429,14 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
raise HomeAssistantAPIError()
|
raise HomeAssistantAPIError()
|
||||||
|
|
||||||
async def check_api_state(self):
|
async def check_api_state(self):
|
||||||
"""Return True if Home-Assistant up and running."""
|
"""Return True if Home Assistant up and running."""
|
||||||
with suppress(HomeAssistantAPIError):
|
with suppress(HomeAssistantAPIError):
|
||||||
async with self.make_request('get', 'api/') as resp:
|
async with self.make_request('get', 'api/') as resp:
|
||||||
if resp.status in (200, 201):
|
if resp.status in (200, 201):
|
||||||
return True
|
return True
|
||||||
err = resp.status
|
err = resp.status
|
||||||
|
|
||||||
_LOGGER.warning("Home-Assistant API config missmatch: %d", err)
|
_LOGGER.warning("Home Assistant API config mismatch: %d", err)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def send_event(self, event_type, event_data=None):
|
async def send_event(self, event_type, event_data=None):
|
||||||
@ -449,7 +449,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
return
|
return
|
||||||
err = resp.status
|
err = resp.status
|
||||||
|
|
||||||
_LOGGER.warning("HomeAssistant event %s fails: %s", event_type, err)
|
_LOGGER.warning("Home Assistant event %s fails: %s", event_type, err)
|
||||||
return HomeAssistantError()
|
return HomeAssistantError()
|
||||||
|
|
||||||
async def _block_till_run(self):
|
async def _block_till_run(self):
|
||||||
@ -479,13 +479,13 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
# 1
|
# 1
|
||||||
# Check if Container is is_running
|
# Check if Container is is_running
|
||||||
if not await self.instance.is_running():
|
if not await self.instance.is_running():
|
||||||
_LOGGER.error("HomeAssistant is crashed!")
|
_LOGGER.error("Home Assistant has crashed!")
|
||||||
break
|
break
|
||||||
|
|
||||||
# 2
|
# 2
|
||||||
# Check if API response
|
# Check if API response
|
||||||
if await self.sys_run_in_executor(check_port):
|
if await self.sys_run_in_executor(check_port):
|
||||||
_LOGGER.info("Detect a running HomeAssistant instance")
|
_LOGGER.info("Detect a running Home Assistant instance")
|
||||||
self._error_state = False
|
self._error_state = False
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -494,17 +494,18 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
if migration_file.exists():
|
if migration_file.exists():
|
||||||
if not migration_progress:
|
if not migration_progress:
|
||||||
migration_progress = True
|
migration_progress = True
|
||||||
_LOGGER.info("HomeAssistant record migration in progress")
|
_LOGGER.info("Home Assistant record migration in progress")
|
||||||
continue
|
continue
|
||||||
elif migration_progress:
|
elif migration_progress:
|
||||||
migration_progress = False # Reset start time
|
migration_progress = False # Reset start time
|
||||||
start_time = time.monotonic()
|
start_time = time.monotonic()
|
||||||
_LOGGER.info("HomeAssistant record migration done")
|
_LOGGER.info("Home Assistant record migration done")
|
||||||
|
|
||||||
# 4
|
# 4
|
||||||
# Timeout
|
# Timeout
|
||||||
if time.monotonic() - start_time > self.wait_boot:
|
if time.monotonic() - start_time > self.wait_boot:
|
||||||
_LOGGER.warning("Don't wait anymore of HomeAssistant startup!")
|
_LOGGER.warning(
|
||||||
|
"Don't wait anymore of Home Assistant startup!")
|
||||||
break
|
break
|
||||||
|
|
||||||
self._error_state = True
|
self._error_state = True
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""HomeAssistant control object."""
|
"""Home Assistant control object."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class Supervisor(CoreSysAttributes):
|
class Supervisor(CoreSysAttributes):
|
||||||
"""Hass core object for handle it."""
|
"""Home Assistant core object for handle it."""
|
||||||
|
|
||||||
def __init__(self, coresys):
|
def __init__(self, coresys):
|
||||||
"""Initialize hass object."""
|
"""Initialize hass object."""
|
||||||
@ -23,9 +23,9 @@ class Supervisor(CoreSysAttributes):
|
|||||||
self.instance = DockerSupervisor(coresys)
|
self.instance = DockerSupervisor(coresys)
|
||||||
|
|
||||||
async def load(self):
|
async def load(self):
|
||||||
"""Prepare HomeAssistant object."""
|
"""Prepare Home Assistant object."""
|
||||||
if not await self.instance.attach():
|
if not await self.instance.attach():
|
||||||
_LOGGER.fatal("Can't setup supervisor docker container!")
|
_LOGGER.fatal("Can't setup Supervisor Docker container!")
|
||||||
await self.instance.cleanup()
|
await self.instance.cleanup()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -35,22 +35,22 @@ class Supervisor(CoreSysAttributes):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
"""Return version of running homeassistant."""
|
"""Return version of running Home Assistant."""
|
||||||
return self.instance.version
|
return self.instance.version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_version(self):
|
def last_version(self):
|
||||||
"""Return last available version of homeassistant."""
|
"""Return last available version of Home Assistant."""
|
||||||
return self.sys_updater.version_hassio
|
return self.sys_updater.version_hassio
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image(self):
|
def image(self):
|
||||||
"""Return image name of hass containter."""
|
"""Return image name of Home Assistant container."""
|
||||||
return self.instance.image
|
return self.instance.image
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def arch(self):
|
def arch(self):
|
||||||
"""Return arch of hass.io containter."""
|
"""Return arch of the Hass.io container."""
|
||||||
return self.instance.arch
|
return self.instance.arch
|
||||||
|
|
||||||
async def update_apparmor(self):
|
async def update_apparmor(self):
|
||||||
@ -79,20 +79,20 @@ class Supervisor(CoreSysAttributes):
|
|||||||
_LOGGER.error("Can't update AppArmor profile!")
|
_LOGGER.error("Can't update AppArmor profile!")
|
||||||
|
|
||||||
async def update(self, version=None):
|
async def update(self, version=None):
|
||||||
"""Update HomeAssistant version."""
|
"""Update Home Assistant version."""
|
||||||
version = version or self.last_version
|
version = version or self.last_version
|
||||||
|
|
||||||
if version == self.sys_supervisor.version:
|
if version == self.sys_supervisor.version:
|
||||||
_LOGGER.warning("Version %s is already installed", version)
|
_LOGGER.warning("Version %s is already installed", version)
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.info("Update supervisor to version %s", version)
|
_LOGGER.info("Update Supervisor to version %s", version)
|
||||||
if await self.instance.install(version):
|
if await self.instance.install(version):
|
||||||
await self.update_apparmor()
|
await self.update_apparmor()
|
||||||
self.sys_loop.call_later(1, self.sys_loop.stop)
|
self.sys_loop.call_later(1, self.sys_loop.stop)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
_LOGGER.error("Update of hass.io fails!")
|
_LOGGER.error("Update of Hass.io fails!")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Multible tasks."""
|
"""A collection of tasks."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ RUN_WATCHDOG_HOMEASSISTANT_API = 300
|
|||||||
|
|
||||||
|
|
||||||
class Tasks(CoreSysAttributes):
|
class Tasks(CoreSysAttributes):
|
||||||
"""Handle Tasks inside HassIO."""
|
"""Handle Tasks inside Hass.io."""
|
||||||
|
|
||||||
def __init__(self, coresys):
|
def __init__(self, coresys):
|
||||||
"""Initialize Tasks."""
|
"""Initialize Tasks."""
|
||||||
@ -58,7 +58,7 @@ class Tasks(CoreSysAttributes):
|
|||||||
_LOGGER.info("All core tasks are scheduled")
|
_LOGGER.info("All core tasks are scheduled")
|
||||||
|
|
||||||
async def _update_addons(self):
|
async def _update_addons(self):
|
||||||
"""Check if an update is available for an addon and update it."""
|
"""Check if an update is available for an Add-on and update it."""
|
||||||
tasks = []
|
tasks = []
|
||||||
for addon in self.sys_addons.list_addons:
|
for addon in self.sys_addons.list_addons:
|
||||||
if not addon.is_installed or not addon.auto_update:
|
if not addon.is_installed or not addon.auto_update:
|
||||||
@ -67,18 +67,18 @@ class Tasks(CoreSysAttributes):
|
|||||||
if addon.version_installed == addon.last_version:
|
if addon.version_installed == addon.last_version:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if addon.test_udpate_schema():
|
if addon.test_update_schema():
|
||||||
tasks.append(addon.update())
|
tasks.append(addon.update())
|
||||||
else:
|
else:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Addon %s will be ignore, schema tests fails", addon.slug)
|
"Add-on %s will be ignore, schema tests fails", addon.slug)
|
||||||
|
|
||||||
if tasks:
|
if tasks:
|
||||||
_LOGGER.info("Addon auto update process %d tasks", len(tasks))
|
_LOGGER.info("Add-on auto update process %d tasks", len(tasks))
|
||||||
await asyncio.wait(tasks)
|
await asyncio.wait(tasks)
|
||||||
|
|
||||||
async def _update_supervisor(self):
|
async def _update_supervisor(self):
|
||||||
"""Check and run update of supervisor hassio."""
|
"""Check and run update of Supervisor Hass.io."""
|
||||||
if not self.sys_supervisor.need_update:
|
if not self.sys_supervisor.need_update:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -91,23 +91,23 @@ class Tasks(CoreSysAttributes):
|
|||||||
await self.sys_supervisor.update()
|
await self.sys_supervisor.update()
|
||||||
|
|
||||||
async def _watchdog_homeassistant_docker(self):
|
async def _watchdog_homeassistant_docker(self):
|
||||||
"""Check running state of docker and start if they is close."""
|
"""Check running state of Docker and start if they is close."""
|
||||||
# if Home-Assistant is active
|
# if Home Assistant is active
|
||||||
if not await self.sys_homeassistant.is_initialize() or \
|
if not await self.sys_homeassistant.is_initialize() or \
|
||||||
not self.sys_homeassistant.watchdog or \
|
not self.sys_homeassistant.watchdog or \
|
||||||
self.sys_homeassistant.error_state:
|
self.sys_homeassistant.error_state:
|
||||||
return
|
return
|
||||||
|
|
||||||
# if Home-Assistant is running
|
# if Home Assistant is running
|
||||||
if self.sys_homeassistant.in_progress or \
|
if self.sys_homeassistant.in_progress or \
|
||||||
await self.sys_homeassistant.is_running():
|
await self.sys_homeassistant.is_running():
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.warning("Watchdog found a problem with Home-Assistant docker!")
|
_LOGGER.warning("Watchdog found a problem with Home Assistant Docker!")
|
||||||
await self.sys_homeassistant.start()
|
await self.sys_homeassistant.start()
|
||||||
|
|
||||||
async def _watchdog_homeassistant_api(self):
|
async def _watchdog_homeassistant_api(self):
|
||||||
"""Create scheduler task for montoring running state of API.
|
"""Create scheduler task for monitoring running state of API.
|
||||||
|
|
||||||
Try 2 times to call API before we restart Home-Assistant. Maybe we had
|
Try 2 times to call API before we restart Home-Assistant. Maybe we had
|
||||||
a delay in our system.
|
a delay in our system.
|
||||||
@ -130,10 +130,10 @@ class Tasks(CoreSysAttributes):
|
|||||||
retry_scan += 1
|
retry_scan += 1
|
||||||
if retry_scan == 1:
|
if retry_scan == 1:
|
||||||
self._cache[HASS_WATCHDOG_API] = retry_scan
|
self._cache[HASS_WATCHDOG_API] = retry_scan
|
||||||
_LOGGER.warning("Watchdog miss API response from Home-Assistant")
|
_LOGGER.warning("Watchdog miss API response from Home Assistant")
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.error("Watchdog found a problem with Home-Assistant API!")
|
_LOGGER.error("Watchdog found a problem with Home Assistant API!")
|
||||||
try:
|
try:
|
||||||
await self.sys_homeassistant.restart()
|
await self.sys_homeassistant.restart()
|
||||||
finally:
|
finally:
|
||||||
|
@ -39,27 +39,27 @@ class Updater(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def version_homeassistant(self):
|
def version_homeassistant(self):
|
||||||
"""Return last version of homeassistant."""
|
"""Return last version of Home Assistant."""
|
||||||
return self._data.get(ATTR_HOMEASSISTANT)
|
return self._data.get(ATTR_HOMEASSISTANT)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version_hassio(self):
|
def version_hassio(self):
|
||||||
"""Return last version of hassio."""
|
"""Return last version of Hass.io."""
|
||||||
return self._data.get(ATTR_HASSIO)
|
return self._data.get(ATTR_HASSIO)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version_hassos(self):
|
def version_hassos(self):
|
||||||
"""Return last version of hassos."""
|
"""Return last version of HassOS."""
|
||||||
return self._data.get(ATTR_HASSOS)
|
return self._data.get(ATTR_HASSOS)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version_hassos_cli(self):
|
def version_hassos_cli(self):
|
||||||
"""Return last version of hassos cli."""
|
"""Return last version of HassOS cli."""
|
||||||
return self._data.get(ATTR_HASSOS_CLI)
|
return self._data.get(ATTR_HASSOS_CLI)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channel(self):
|
def channel(self):
|
||||||
"""Return upstream channel of hassio instance."""
|
"""Return upstream channel of Hass.io instance."""
|
||||||
return self._data[ATTR_CHANNEL]
|
return self._data[ATTR_CHANNEL]
|
||||||
|
|
||||||
@channel.setter
|
@channel.setter
|
||||||
@ -69,7 +69,7 @@ class Updater(JsonConfig, CoreSysAttributes):
|
|||||||
|
|
||||||
@AsyncThrottle(timedelta(seconds=60))
|
@AsyncThrottle(timedelta(seconds=60))
|
||||||
async def fetch_data(self):
|
async def fetch_data(self):
|
||||||
"""Fetch current versions from github.
|
"""Fetch current versions from Github.
|
||||||
|
|
||||||
Is a coroutine.
|
Is a coroutine.
|
||||||
"""
|
"""
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Tools file for HassIO."""
|
"""Tools file for Hass.io."""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
@ -25,7 +25,8 @@ def process_lock(method):
|
|||||||
"""Return api wrapper."""
|
"""Return api wrapper."""
|
||||||
if api.lock.locked():
|
if api.lock.locked():
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"Can't excute %s while a task is in progress", method.__name__)
|
"Can't execute %s while a task is in progress",
|
||||||
|
method.__name__)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async with api.lock:
|
async with api.lock:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Some functions around apparmor profiles."""
|
"""Some functions around AppArmor profiles."""
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ def get_profile_name(profile_file):
|
|||||||
continue
|
continue
|
||||||
profiles.add(match.group(1))
|
profiles.add(match.group(1))
|
||||||
except OSError as err:
|
except OSError as err:
|
||||||
_LOGGER.error("Can't read apparmor profile: %s", err)
|
_LOGGER.error("Can't read AppArmor profile: %s", err)
|
||||||
raise AppArmorFileError()
|
raise AppArmorFileError()
|
||||||
|
|
||||||
if len(profiles) != 1:
|
if len(profiles) != 1:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Tools file for HassIO."""
|
"""Tools file for Hass.io."""
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
@ -58,5 +58,5 @@ def parse_datetime(dt_str):
|
|||||||
|
|
||||||
|
|
||||||
def utcnow():
|
def utcnow():
|
||||||
"""Returns current timestamp including timezone."""
|
"""Return the current timestamp including timezone."""
|
||||||
return datetime.now(UTC)
|
return datetime.now(UTC)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
"""Tools file for HassIO."""
|
"""Tools file for Hass.io."""
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -9,14 +9,14 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def write_json_file(jsonfile, data):
|
def write_json_file(jsonfile, data):
|
||||||
"""Write a json file."""
|
"""Write a JSON file."""
|
||||||
json_str = json.dumps(data, indent=2)
|
json_str = json.dumps(data, indent=2)
|
||||||
with jsonfile.open('w') as conf_file:
|
with jsonfile.open('w') as conf_file:
|
||||||
conf_file.write(json_str)
|
conf_file.write(json_str)
|
||||||
|
|
||||||
|
|
||||||
def read_json_file(jsonfile):
|
def read_json_file(jsonfile):
|
||||||
"""Read a json file and return a dict."""
|
"""Read a JSON file and return a dict."""
|
||||||
with jsonfile.open('r') as cfile:
|
with jsonfile.open('r') as cfile:
|
||||||
return json.loads(cfile.read())
|
return json.loads(cfile.read())
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ class JsonConfig:
|
|||||||
self.read_data()
|
self.read_data()
|
||||||
|
|
||||||
def reset_data(self):
|
def reset_data(self):
|
||||||
"""Reset json file to default."""
|
"""Reset JSON file to default."""
|
||||||
try:
|
try:
|
||||||
self._data = self._schema({})
|
self._data = self._schema({})
|
||||||
except vol.Invalid as ex:
|
except vol.Invalid as ex:
|
||||||
@ -41,7 +41,7 @@ class JsonConfig:
|
|||||||
self._file, humanize_error(self._data, ex))
|
self._file, humanize_error(self._data, ex))
|
||||||
|
|
||||||
def read_data(self):
|
def read_data(self):
|
||||||
"""Read json file & validate."""
|
"""Read JSON file & validate."""
|
||||||
if self._file.is_file():
|
if self._file.is_file():
|
||||||
try:
|
try:
|
||||||
self._data = read_json_file(self._file)
|
self._data = read_json_file(self._file)
|
||||||
@ -61,7 +61,7 @@ class JsonConfig:
|
|||||||
self._data = self._schema({})
|
self._data = self._schema({})
|
||||||
|
|
||||||
def save_data(self):
|
def save_data(self):
|
||||||
"""Store data to config file."""
|
"""Store data to configuration file."""
|
||||||
# Validate
|
# Validate
|
||||||
try:
|
try:
|
||||||
self._data = self._schema(self._data)
|
self._data = self._schema(self._data)
|
||||||
@ -78,4 +78,5 @@ class JsonConfig:
|
|||||||
try:
|
try:
|
||||||
write_json_file(self._file, self._data)
|
write_json_file(self._file, self._data)
|
||||||
except (OSError, json.JSONDecodeError) as err:
|
except (OSError, json.JSONDecodeError) as err:
|
||||||
_LOGGER.error("Can't store config in %s: %s", self._file, err)
|
_LOGGER.error(
|
||||||
|
"Can't store configuration in %s: %s", self._file, err)
|
||||||
|
@ -24,7 +24,7 @@ CHANNELS = vol.In([CHANNEL_STABLE, CHANNEL_BETA, CHANNEL_DEV])
|
|||||||
|
|
||||||
|
|
||||||
def validate_repository(repository):
|
def validate_repository(repository):
|
||||||
"""Validate a valide repository."""
|
"""Validate a valid repository."""
|
||||||
data = RE_REPOSITORY.match(repository)
|
data = RE_REPOSITORY.match(repository)
|
||||||
if not data:
|
if not data:
|
||||||
raise vol.Invalid("No valid repository format!")
|
raise vol.Invalid("No valid repository format!")
|
||||||
@ -55,7 +55,7 @@ def validate_timezone(timezone):
|
|||||||
|
|
||||||
# pylint: disable=inconsistent-return-statements
|
# pylint: disable=inconsistent-return-statements
|
||||||
def convert_to_docker_ports(data):
|
def convert_to_docker_ports(data):
|
||||||
"""Convert data into docker port list."""
|
"""Convert data into Docker port list."""
|
||||||
# dynamic ports
|
# dynamic ports
|
||||||
if data is None:
|
if data is None:
|
||||||
return None
|
return None
|
||||||
@ -72,7 +72,7 @@ def convert_to_docker_ports(data):
|
|||||||
if isinstance(data, list) and len(data) == 2:
|
if isinstance(data, list) and len(data) == 2:
|
||||||
return (vol.Coerce(str)(data[0]), NETWORK_PORT(data[1]))
|
return (vol.Coerce(str)(data[0]), NETWORK_PORT(data[1]))
|
||||||
|
|
||||||
raise vol.Invalid("Can't validate docker host settings")
|
raise vol.Invalid("Can't validate Docker host settings")
|
||||||
|
|
||||||
|
|
||||||
DOCKER_PORTS = vol.Schema({
|
DOCKER_PORTS = vol.Schema({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user