From a30063e85c39223811999b44bac2f0e7c2cdf71c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 30 Mar 2020 12:38:37 +0200 Subject: [PATCH 01/17] Bump version to 215 --- supervisor/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supervisor/const.py b/supervisor/const.py index 25dbb785f..7a0c33732 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -3,7 +3,7 @@ from enum import Enum from ipaddress import ip_network from pathlib import Path -SUPERVISOR_VERSION = "214" +SUPERVISOR_VERSION = "215" URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons" From 1228baebf4cc781cdb701d660cd1b51f99310844 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 2 Apr 2020 18:00:17 +0200 Subject: [PATCH 02/17] Cleanup host groups (#1623) --- supervisor/docker/audio.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/supervisor/docker/audio.py b/supervisor/docker/audio.py index 520c1675a..e5b4cb752 100644 --- a/supervisor/docker/audio.py +++ b/supervisor/docker/audio.py @@ -31,8 +31,7 @@ class DockerAudio(DockerInterface, CoreSysAttributes): def volumes(self) -> Dict[str, Dict[str, str]]: """Return Volumes for the mount.""" volumes = { - str(self.sys_config.path_extern_audio): {"bind": "/data", "mode": "rw"}, - "/etc/group": {"bind": "/host/group", "mode": "ro"}, + str(self.sys_config.path_extern_audio): {"bind": "/data", "mode": "rw"} } # SND support From 61fec8b2905c76a5dbd7af9ad81e519add580624 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 3 Apr 2020 13:44:03 +0200 Subject: [PATCH 03/17] Bump cryptography from 2.8 to 2.9 (#1625) Bumps [cryptography](https://github.com/pyca/cryptography) from 2.8 to 2.9. - [Release notes](https://github.com/pyca/cryptography/releases) - [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/2.8...2.9) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c15237824..5371558a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ attrs==19.3.0 cchardet==2.1.6 colorlog==4.1.0 cpe==1.2.1 -cryptography==2.8 +cryptography==2.9 docker==4.2.0 gitpython==3.1.0 jinja2==2.11.1 From 387e0ad03e5f6e84099a607d42f41c0d0f6f1798 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 3 Apr 2020 14:27:30 +0200 Subject: [PATCH 04/17] Adjust dbus support for pulse (#1626) --- supervisor/docker/audio.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/supervisor/docker/audio.py b/supervisor/docker/audio.py index e5b4cb752..ccc71e079 100644 --- a/supervisor/docker/audio.py +++ b/supervisor/docker/audio.py @@ -31,7 +31,8 @@ class DockerAudio(DockerInterface, CoreSysAttributes): def volumes(self) -> Dict[str, Dict[str, str]]: """Return Volumes for the mount.""" volumes = { - str(self.sys_config.path_extern_audio): {"bind": "/data", "mode": "rw"} + str(self.sys_config.path_extern_audio): {"bind": "/data", "mode": "rw"}, + "/run/dbus": {"bind": "/run/dbus", "mode": "ro"}, } # SND support From 9350e4f961b4dc42ac6d84d505408e95b5f46dd9 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 3 Apr 2020 17:12:18 +0200 Subject: [PATCH 05/17] Store image on local config (#1627) * Store image on local config * Fix api validate * Fix install handling --- API.md | 1 - supervisor/api/homeassistant.py | 10 +++------- supervisor/audio.py | 16 +++++++++++++++- supervisor/cli.py | 16 +++++++++++++++- supervisor/config.py | 4 ++-- supervisor/const.py | 1 - supervisor/dns.py | 18 ++++++++++++++++-- supervisor/docker/audio.py | 2 +- supervisor/docker/cli.py | 2 +- supervisor/docker/dns.py | 2 +- supervisor/homeassistant.py | 16 ++++------------ supervisor/snapshots/snapshot.py | 16 +++------------- supervisor/snapshots/validate.py | 4 +--- supervisor/validate.py | 14 +++++++++----- 14 files changed, 71 insertions(+), 51 deletions(-) diff --git a/API.md b/API.md index b1eb768b5..305417f35 100644 --- a/API.md +++ b/API.md @@ -368,7 +368,6 @@ Trigger an udev reload "machine": "Image machine type", "ip_address": "ip address", "image": "str", - "custom": "bool -> if custom image", "boot": "bool", "port": 8123, "ssl": "bool", diff --git a/supervisor/api/homeassistant.py b/supervisor/api/homeassistant.py index ecf323a31..373c3886b 100644 --- a/supervisor/api/homeassistant.py +++ b/supervisor/api/homeassistant.py @@ -14,10 +14,8 @@ from ..const import ( ATTR_BLK_WRITE, ATTR_BOOT, ATTR_CPU_PERCENT, - ATTR_CUSTOM, ATTR_IMAGE, ATTR_IP_ADDRESS, - ATTR_VERSION_LATEST, ATTR_MACHINE, ATTR_MEMORY_LIMIT, ATTR_MEMORY_PERCENT, @@ -28,6 +26,7 @@ from ..const import ( ATTR_REFRESH_TOKEN, ATTR_SSL, ATTR_VERSION, + ATTR_VERSION_LATEST, ATTR_WAIT_BOOT, ATTR_WATCHDOG, CONTENT_TYPE_BINARY, @@ -43,8 +42,7 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) SCHEMA_OPTIONS = vol.Schema( { vol.Optional(ATTR_BOOT): vol.Boolean(), - vol.Inclusive(ATTR_IMAGE, "custom_hass"): vol.Maybe(docker_image), - vol.Inclusive(ATTR_VERSION_LATEST, "custom_hass"): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, vol.Optional(ATTR_PORT): network_port, vol.Optional(ATTR_SSL): vol.Boolean(), vol.Optional(ATTR_WATCHDOG): vol.Boolean(), @@ -71,7 +69,6 @@ class APIHomeAssistant(CoreSysAttributes): ATTR_IP_ADDRESS: str(self.sys_homeassistant.ip_address), ATTR_ARCH: self.sys_homeassistant.arch, ATTR_IMAGE: self.sys_homeassistant.image, - ATTR_CUSTOM: self.sys_homeassistant.is_custom_image, ATTR_BOOT: self.sys_homeassistant.boot, ATTR_PORT: self.sys_homeassistant.api_port, ATTR_SSL: self.sys_homeassistant.api_ssl, @@ -88,9 +85,8 @@ class APIHomeAssistant(CoreSysAttributes): """Set Home Assistant options.""" body = await api_validate(SCHEMA_OPTIONS, request) - if ATTR_IMAGE in body and ATTR_VERSION_LATEST in body: + if ATTR_IMAGE in body: self.sys_homeassistant.image = body[ATTR_IMAGE] - self.sys_homeassistant.latest_version = body[ATTR_VERSION_LATEST] if ATTR_BOOT in body: self.sys_homeassistant.boot = body[ATTR_BOOT] diff --git a/supervisor/audio.py b/supervisor/audio.py index 3a4adfe61..14374857b 100644 --- a/supervisor/audio.py +++ b/supervisor/audio.py @@ -8,7 +8,7 @@ from typing import Awaitable, Optional import jinja2 -from .const import ATTR_VERSION, FILE_HASSIO_AUDIO +from .const import ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_AUDIO from .coresys import CoreSys, CoreSysAttributes from .docker.audio import DockerAudio from .docker.stats import DockerStats @@ -52,6 +52,18 @@ class Audio(JsonConfig, CoreSysAttributes): """Set current version of Audio.""" self._data[ATTR_VERSION] = value + @property + def image(self) -> str: + """Return current image of Audio.""" + if self._data.get(ATTR_IMAGE): + return self._data[ATTR_IMAGE] + return f"homeassistant/{self.sys_arch.supervisor}-hassio-audio" + + @image.setter + def image(self, value: str) -> None: + """Return current image of Audio.""" + self._data[ATTR_IMAGE] = value + @property def latest_version(self) -> Optional[str]: """Return latest version of Audio.""" @@ -84,6 +96,7 @@ class Audio(JsonConfig, CoreSysAttributes): await self.install() else: self.version = self.instance.version + self.image = self.instance.image self.save_data() # Run PulseAudio @@ -122,6 +135,7 @@ class Audio(JsonConfig, CoreSysAttributes): _LOGGER.info("Audio plugin now installed") self.version = self.instance.version + self.image = self.instance.image self.save_data() async def update(self, version: Optional[str] = None) -> None: diff --git a/supervisor/cli.py b/supervisor/cli.py index 4872d183a..80cf955c1 100644 --- a/supervisor/cli.py +++ b/supervisor/cli.py @@ -5,7 +5,7 @@ import logging import secrets from typing import Awaitable, Optional -from .const import ATTR_ACCESS_TOKEN, ATTR_VERSION, FILE_HASSIO_CLI +from .const import ATTR_ACCESS_TOKEN, ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_CLI from .coresys import CoreSys, CoreSysAttributes from .docker.cli import DockerCli from .docker.stats import DockerStats @@ -35,6 +35,18 @@ class HaCli(CoreSysAttributes, JsonConfig): """Set current version of cli.""" self._data[ATTR_VERSION] = value + @property + def image(self) -> str: + """Return current image of cli.""" + if self._data.get(ATTR_IMAGE): + return self._data[ATTR_IMAGE] + return f"homeassistant/{self.sys_arch.supervisor}-hassio-cli" + + @image.setter + def image(self, value: str) -> None: + """Return current image of cli.""" + self._data[ATTR_IMAGE] = value + @property def latest_version(self) -> str: """Return version of latest cli.""" @@ -72,6 +84,7 @@ class HaCli(CoreSysAttributes, JsonConfig): await self.install() else: self.version = self.instance.version + self.image = self.instance.image self.save_data() # Run PulseAudio @@ -96,6 +109,7 @@ class HaCli(CoreSysAttributes, JsonConfig): _LOGGER.info("cli plugin now installed") self.version = self.instance.version + self.image = self.instance.image self.save_data() async def update(self, version: Optional[str] = None) -> None: diff --git a/supervisor/config.py b/supervisor/config.py index 754e029ba..d13222848 100644 --- a/supervisor/config.py +++ b/supervisor/config.py @@ -17,7 +17,7 @@ from .const import ( ) from .utils.dt import parse_datetime from .utils.json import JsonConfig -from .validate import SCHEMA_HASSIO_CONFIG +from .validate import SCHEMA_SUPERVISOR_CONFIG _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -45,7 +45,7 @@ class CoreConfig(JsonConfig): def __init__(self): """Initialize config object.""" - super().__init__(FILE_HASSIO_CONFIG, SCHEMA_HASSIO_CONFIG) + super().__init__(FILE_HASSIO_CONFIG, SCHEMA_SUPERVISOR_CONFIG) @property def timezone(self): diff --git a/supervisor/const.py b/supervisor/const.py index 7a0c33732..c4067278f 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -147,7 +147,6 @@ ATTR_SIZE = "size" ATTR_TYPE = "type" ATTR_TIMEOUT = "timeout" ATTR_AUTO_UPDATE = "auto_update" -ATTR_CUSTOM = "custom" ATTR_VIDEO = "video" ATTR_AUDIO = "audio" ATTR_AUDIO_INPUT = "audio_input" diff --git a/supervisor/dns.py b/supervisor/dns.py index f81ee3ceb..516fe2ed3 100644 --- a/supervisor/dns.py +++ b/supervisor/dns.py @@ -10,14 +10,14 @@ import attr import jinja2 import voluptuous as vol -from .const import ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, FILE_HASSIO_DNS +from .const import ATTR_IMAGE, ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, FILE_HASSIO_DNS from .coresys import CoreSys, CoreSysAttributes from .docker.dns import DockerDNS from .docker.stats import DockerStats from .exceptions import CoreDNSError, CoreDNSUpdateError, DockerAPIError from .misc.forwarder import DNSForward from .utils.json import JsonConfig -from .validate import dns_url, SCHEMA_DNS_CONFIG +from .validate import SCHEMA_DNS_CONFIG, dns_url _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -79,6 +79,18 @@ class CoreDNS(JsonConfig, CoreSysAttributes): """Return current version of DNS.""" self._data[ATTR_VERSION] = value + @property + def image(self) -> str: + """Return current image of DNS.""" + if self._data.get(ATTR_IMAGE): + return self._data[ATTR_IMAGE] + return f"homeassistant/{self.sys_arch.supervisor}-hassio-dns" + + @image.setter + def image(self, value: str) -> None: + """Return current image of DNS.""" + self._data[ATTR_IMAGE] = value + @property def latest_version(self) -> Optional[str]: """Return latest version of CoreDNS.""" @@ -115,6 +127,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes): await self.install() else: self.version = self.instance.version + self.image = self.instance.image self.save_data() # Start DNS forwarder @@ -161,6 +174,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes): _LOGGER.info("CoreDNS plugin now installed") self.version = self.instance.version + self.image = self.instance.image self.save_data() # Init Hosts diff --git a/supervisor/docker/audio.py b/supervisor/docker/audio.py index ccc71e079..efa93099f 100644 --- a/supervisor/docker/audio.py +++ b/supervisor/docker/audio.py @@ -20,7 +20,7 @@ class DockerAudio(DockerInterface, CoreSysAttributes): @property def image(self) -> str: """Return name of Supervisor Audio image.""" - return f"homeassistant/{self.sys_arch.supervisor}-hassio-audio" + return self.sys_audio.image @property def name(self) -> str: diff --git a/supervisor/docker/cli.py b/supervisor/docker/cli.py index 08ebca7d3..08866b46e 100644 --- a/supervisor/docker/cli.py +++ b/supervisor/docker/cli.py @@ -18,7 +18,7 @@ class DockerCli(DockerInterface, CoreSysAttributes): @property def image(self): """Return name of HA cli image.""" - return f"homeassistant/{self.sys_arch.supervisor}-hassio-cli" + return self.sys_cli.image @property def name(self) -> str: diff --git a/supervisor/docker/dns.py b/supervisor/docker/dns.py index 9d02a76bb..a20068035 100644 --- a/supervisor/docker/dns.py +++ b/supervisor/docker/dns.py @@ -18,7 +18,7 @@ class DockerDNS(DockerInterface, CoreSysAttributes): @property def image(self) -> str: """Return name of Supervisor DNS image.""" - return f"homeassistant/{self.sys_arch.supervisor}-hassio-dns" + return self.sys_dns.image @property def name(self) -> str: diff --git a/supervisor/homeassistant.py b/supervisor/homeassistant.py index 11474c8cc..62bf9581e 100644 --- a/supervisor/homeassistant.py +++ b/supervisor/homeassistant.py @@ -91,6 +91,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): await self.install_landingpage() else: self.version = self.instance.version + self.image = self.instance.image self.save_data() @property @@ -163,8 +164,6 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): @property def latest_version(self) -> str: """Return last available version of Home Assistant.""" - if self.is_custom_image: - return self._data.get(ATTR_VERSION_LATEST) return self.sys_updater.version_homeassistant @latest_version.setter @@ -183,17 +182,9 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): return os.environ["HOMEASSISTANT_REPOSITORY"] @image.setter - def image(self, value: str): + def image(self, value: str) -> None: """Set image name of Home Assistant container.""" - if value: - self._data[ATTR_IMAGE] = value - else: - self._data.pop(ATTR_IMAGE, None) - - @property - def is_custom_image(self) -> bool: - """Return True if a custom image is used.""" - return all(attr in self._data for attr in (ATTR_IMAGE, ATTR_VERSION_LATEST)) + self._data[ATTR_IMAGE] = value @property def version(self) -> Optional[str]: @@ -304,6 +295,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): _LOGGER.info("Home Assistant docker now installed") self.version = self.instance.version + self.image = self.instance.image self.save_data() # finishing diff --git a/supervisor/snapshots/snapshot.py b/supervisor/snapshots/snapshot.py index cb0c25127..bb74af991 100644 --- a/supervisor/snapshots/snapshot.py +++ b/supervisor/snapshots/snapshot.py @@ -24,7 +24,6 @@ from ..const import ( ATTR_FOLDERS, ATTR_HOMEASSISTANT, ATTR_IMAGE, - ATTR_VERSION_LATEST, ATTR_NAME, ATTR_PORT, ATTR_PROTECTED, @@ -430,13 +429,7 @@ class Snapshot(CoreSysAttributes): self.homeassistant[ATTR_WATCHDOG] = self.sys_homeassistant.watchdog self.homeassistant[ATTR_BOOT] = self.sys_homeassistant.boot self.homeassistant[ATTR_WAIT_BOOT] = self.sys_homeassistant.wait_boot - - # Custom image - if self.sys_homeassistant.is_custom_image: - self.homeassistant[ATTR_IMAGE] = self.sys_homeassistant.image - self.homeassistant[ - ATTR_VERSION_LATEST - ] = self.sys_homeassistant.latest_version + self.homeassistant[ATTR_IMAGE] = self.sys_homeassistant.image # API/Proxy self.homeassistant[ATTR_PORT] = self.sys_homeassistant.api_port @@ -455,12 +448,9 @@ class Snapshot(CoreSysAttributes): self.sys_homeassistant.boot = self.homeassistant[ATTR_BOOT] self.sys_homeassistant.wait_boot = self.homeassistant[ATTR_WAIT_BOOT] - # Custom image - if self.homeassistant.get(ATTR_IMAGE): + # Was not needed before + if self.homeassistant[ATTR_IMAGE]: self.sys_homeassistant.image = self.homeassistant[ATTR_IMAGE] - self.sys_homeassistant.latest_version = self.homeassistant[ - ATTR_VERSION_LATEST - ] # API/Proxy self.sys_homeassistant.api_port = self.homeassistant[ATTR_PORT] diff --git a/supervisor/snapshots/validate.py b/supervisor/snapshots/validate.py index 54f32665d..b3807ecaf 100644 --- a/supervisor/snapshots/validate.py +++ b/supervisor/snapshots/validate.py @@ -11,7 +11,6 @@ from ..const import ( ATTR_FOLDERS, ATTR_HOMEASSISTANT, ATTR_IMAGE, - ATTR_VERSION_LATEST, ATTR_NAME, ATTR_PORT, ATTR_PROTECTED, @@ -60,8 +59,7 @@ SCHEMA_SNAPSHOT = vol.Schema( vol.Optional(ATTR_HOMEASSISTANT, default=dict): vol.Schema( { vol.Optional(ATTR_VERSION): vol.Coerce(str), - vol.Inclusive(ATTR_IMAGE, "custom_hass"): docker_image, - vol.Inclusive(ATTR_VERSION_LATEST, "custom_hass"): vol.Coerce(str), + vol.Optional(ATTR_IMAGE): docker_image, vol.Optional(ATTR_BOOT, default=True): vol.Boolean(), vol.Optional(ATTR_SSL, default=False): vol.Boolean(), vol.Optional(ATTR_PORT, default=8123): network_port, diff --git a/supervisor/validate.py b/supervisor/validate.py index 12474f0a5..e7a28c199 100644 --- a/supervisor/validate.py +++ b/supervisor/validate.py @@ -22,7 +22,6 @@ from .const import ( ATTR_HOMEASSISTANT, ATTR_IMAGE, ATTR_LAST_BOOT, - ATTR_VERSION_LATEST, ATTR_LOGGING, ATTR_PORT, ATTR_PORTS, @@ -102,10 +101,9 @@ SCHEMA_HASS_CONFIG = vol.Schema( { vol.Optional(ATTR_UUID, default=lambda: uuid.uuid4().hex): uuid_match, vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, vol.Optional(ATTR_ACCESS_TOKEN): token, vol.Optional(ATTR_BOOT, default=True): vol.Boolean(), - vol.Inclusive(ATTR_IMAGE, "custom_hass"): docker_image, - vol.Inclusive(ATTR_VERSION_LATEST, "custom_hass"): vol.Coerce(str), vol.Optional(ATTR_PORT, default=8123): network_port, vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(vol.Coerce(str)), vol.Optional(ATTR_SSL, default=False): vol.Boolean(), @@ -137,7 +135,7 @@ SCHEMA_UPDATER_CONFIG = vol.Schema( # pylint: disable=no-value-for-parameter -SCHEMA_HASSIO_CONFIG = vol.Schema( +SCHEMA_SUPERVISOR_CONFIG = vol.Schema( { vol.Optional(ATTR_TIMEZONE, default="UTC"): validate_timezone, vol.Optional(ATTR_LAST_BOOT): vol.Coerce(str), @@ -173,6 +171,7 @@ SCHEMA_INGRESS_CONFIG = vol.Schema( SCHEMA_DNS_CONFIG = vol.Schema( { vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, vol.Optional(ATTR_SERVERS, default=list): dns_server_list, }, extra=vol.REMOVE_EXTRA, @@ -180,13 +179,18 @@ SCHEMA_DNS_CONFIG = vol.Schema( SCHEMA_AUDIO_CONFIG = vol.Schema( - {vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str))}, extra=vol.REMOVE_EXTRA, + { + vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, + }, + extra=vol.REMOVE_EXTRA, ) SCHEMA_CLI_CONFIG = vol.Schema( { vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, vol.Optional(ATTR_ACCESS_TOKEN): token, }, extra=vol.REMOVE_EXTRA, From fcebc9d1ed8c88e4556b6f5ce40dd64b90486662 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 5 Apr 2020 00:47:05 +0200 Subject: [PATCH 06/17] Use updater for image data (#1628) * Use updater for image data * Fix message * Fix handling * Update code * fix lint * fix names * Fix log * Fix error log * make it better --- supervisor/api/supervisor.py | 4 +-- supervisor/audio.py | 20 ++++++----- supervisor/bootstrap.py | 31 ++++++++++++---- supervisor/cli.py | 22 ++++++++---- supervisor/config.py | 25 ++++++------- supervisor/const.py | 7 +++- supervisor/core.py | 8 +++-- supervisor/coresys.py | 34 +++++++++++++----- supervisor/dns.py | 18 ++++++---- supervisor/docker/cli.py | 2 +- supervisor/docker/interface.py | 16 +++++++-- supervisor/homeassistant.py | 34 +++++++++--------- supervisor/ingress.py | 5 +++ supervisor/snapshots/snapshot.py | 4 +-- supervisor/supervisor.py | 6 ++-- supervisor/updater.py | 61 +++++++++++++++++++++++++++++--- supervisor/validate.py | 14 ++++++-- 17 files changed, 226 insertions(+), 85 deletions(-) diff --git a/supervisor/api/supervisor.py b/supervisor/api/supervisor.py index 55e37225d..33b71613b 100644 --- a/supervisor/api/supervisor.py +++ b/supervisor/api/supervisor.py @@ -92,7 +92,7 @@ class APISupervisor(CoreSysAttributes): return { ATTR_VERSION: SUPERVISOR_VERSION, - ATTR_VERSION_LATEST: self.sys_updater.version_hassio, + ATTR_VERSION_LATEST: self.sys_updater.version_supervisor, ATTR_CHANNEL: self.sys_updater.channel, ATTR_ARCH: self.sys_supervisor.arch, ATTR_IP_ADDRESS: str(self.sys_supervisor.ip_address), @@ -153,7 +153,7 @@ class APISupervisor(CoreSysAttributes): async def update(self, request: web.Request) -> None: """Update Supervisor OS.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self.sys_updater.version_hassio) + version = body.get(ATTR_VERSION, self.sys_updater.version_supervisor) if version == self.sys_supervisor.version: raise APIError("Version {} is already in use".format(version)) diff --git a/supervisor/audio.py b/supervisor/audio.py index 14374857b..d98c740d4 100644 --- a/supervisor/audio.py +++ b/supervisor/audio.py @@ -128,36 +128,40 @@ class Audio(JsonConfig, CoreSysAttributes): if self.latest_version: with suppress(DockerAPIError): - await self.instance.install(self.latest_version) + await self.instance.install( + self.latest_version, image=self.sys_updater.image_audio + ) break _LOGGER.warning("Error on install Audio plugin. Retry in 30sec") await asyncio.sleep(30) _LOGGER.info("Audio plugin now installed") self.version = self.instance.version - self.image = self.instance.image + self.image = self.sys_updater.image_audio self.save_data() async def update(self, version: Optional[str] = None) -> None: """Update Audio plugin.""" version = version or self.latest_version + old_image = self.image if version == self.version: _LOGGER.warning("Version %s is already installed for Audio", version) return try: - await self.instance.update(version) + await self.instance.update(version, image=self.sys_updater.image_audio) except DockerAPIError: _LOGGER.error("Audio update fails") raise AudioUpdateError() from None else: - # Cleanup - with suppress(DockerAPIError): - await self.instance.cleanup() + self.version = version + self.image = self.sys_updater.image_audio + self.save_data() - self.version = version - self.save_data() + # Cleanup + with suppress(DockerAPIError): + await self.instance.cleanup(old_image=old_image) # Start Audio await self.start() diff --git a/supervisor/bootstrap.py b/supervisor/bootstrap.py index 26ae31352..6c23e68e2 100644 --- a/supervisor/bootstrap.py +++ b/supervisor/bootstrap.py @@ -12,7 +12,14 @@ from .api import RestAPI from .arch import CpuArch from .auth import Auth from .audio import Audio -from .const import SOCKET_DOCKER, UpdateChannels +from .const import ( + SOCKET_DOCKER, + UpdateChannels, + ENV_SUPERVISOR_SHARE, + ENV_SUPERVISOR_NAME, + ENV_HOMEASSISTANT_REPOSITORY, + ENV_SUPERVISOR_MACHINE, +) from .core import Core from .cli import HaCli from .coresys import CoreSys @@ -35,9 +42,6 @@ from .utils.dt import fetch_timezone _LOGGER: logging.Logger = logging.getLogger(__name__) -ENV_SHARE = "SUPERVISOR_SHARE" -ENV_NAME = "SUPERVISOR_NAME" -ENV_REPO = "HOMEASSISTANT_REPOSITORY" MACHINE_ID = Path("/etc/machine-id") @@ -81,6 +85,13 @@ async def initialize_coresys(): if coresys.config.timezone == "UTC": coresys.config.timezone = await fetch_timezone(coresys.websession) + # Set machine type + if os.environ.get(ENV_SUPERVISOR_MACHINE): + coresys.machine = os.environ[ENV_SUPERVISOR_MACHINE] + elif os.environ.get(ENV_HOMEASSISTANT_REPOSITORY): + coresys.machine = os.environ[ENV_HOMEASSISTANT_REPOSITORY][14:-14] + _LOGGER.info("Setup coresys for machine: %s", coresys.machine) + return coresys @@ -165,7 +176,7 @@ def migrate_system_env(coresys: CoreSys): config = coresys.config # hass.io 0.37 -> 0.38 - old_build = Path(config.path_hassio, "addons/build") + old_build = Path(config.path_supervisor, "addons/build") if old_build.is_dir(): try: old_build.rmdir() @@ -202,12 +213,20 @@ def initialize_logging(): def check_environment() -> None: """Check if all environment are exists.""" # check environment variables - for key in (ENV_SHARE, ENV_NAME, ENV_REPO): + for key in (ENV_SUPERVISOR_SHARE, ENV_SUPERVISOR_NAME): try: os.environ[key] except KeyError: _LOGGER.fatal("Can't find %s in env!", key) + # Check Machine info + if not os.environ.get(ENV_HOMEASSISTANT_REPOSITORY) and not os.environ.get( + ENV_SUPERVISOR_MACHINE + ): + _LOGGER.fatal("Can't find any kind of machine/homeassistant details!") + elif not os.environ.get(ENV_SUPERVISOR_MACHINE): + _LOGGER.info("Use the old homeassistant repository for machine extraction") + # check docker socket if not SOCKET_DOCKER.is_socket(): _LOGGER.fatal("Can't find Docker socket!") diff --git a/supervisor/cli.py b/supervisor/cli.py index 80cf955c1..af7171166 100644 --- a/supervisor/cli.py +++ b/supervisor/cli.py @@ -102,36 +102,44 @@ class HaCli(CoreSysAttributes, JsonConfig): if self.latest_version: with suppress(DockerAPIError): - await self.instance.install(self.latest_version, latest=True) + await self.instance.install( + self.latest_version, + image=self.sys_updater.image_cli, + latest=True, + ) break _LOGGER.warning("Error on install cli plugin. Retry in 30sec") await asyncio.sleep(30) _LOGGER.info("cli plugin now installed") self.version = self.instance.version - self.image = self.instance.image + self.image = self.sys_updater.image_cli self.save_data() async def update(self, version: Optional[str] = None) -> None: """Update local HA cli.""" version = version or self.latest_version + old_image = self.image if version == self.version: _LOGGER.warning("Version %s is already installed for cli", version) return try: - await self.instance.update(version, latest=True) + await self.instance.update( + version, image=self.sys_updater.image_cli, latest=True + ) except DockerAPIError: _LOGGER.error("HA cli update fails") raise CliUpdateError() from None + else: + self.version = version + self.image = self.sys_updater.image_cli + self.save_data() # Cleanup with suppress(DockerAPIError): - await self.instance.cleanup() - - self.version = version - self.save_data() + await self.instance.cleanup(old_image=old_image) # Start cli await self.start() diff --git a/supervisor/config.py b/supervisor/config.py index d13222848..e4fb771d5 100644 --- a/supervisor/config.py +++ b/supervisor/config.py @@ -12,6 +12,7 @@ from .const import ( ATTR_LOGGING, ATTR_TIMEZONE, ATTR_WAIT_BOOT, + ENV_SUPERVISOR_SHARE, FILE_HASSIO_CONFIG, SUPERVISOR_DATA, ) @@ -119,19 +120,19 @@ class CoreConfig(JsonConfig): self._data[ATTR_LAST_BOOT] = value.isoformat() @property - def path_hassio(self): + def path_supervisor(self): """Return Supervisor data path.""" return SUPERVISOR_DATA @property - def path_extern_hassio(self): + def path_extern_supervisor(self): """Return Supervisor data path external for Docker.""" - return PurePath(os.environ["SUPERVISOR_SHARE"]) + return PurePath(os.environ[ENV_SUPERVISOR_SHARE]) @property def path_extern_homeassistant(self): """Return config path external for Docker.""" - return str(PurePath(self.path_extern_hassio, HOMEASSISTANT_CONFIG)) + return str(PurePath(self.path_extern_supervisor, HOMEASSISTANT_CONFIG)) @property def path_homeassistant(self): @@ -141,7 +142,7 @@ class CoreConfig(JsonConfig): @property def path_extern_ssl(self): """Return SSL path external for Docker.""" - return str(PurePath(self.path_extern_hassio, HASSIO_SSL)) + return str(PurePath(self.path_extern_supervisor, HASSIO_SSL)) @property def path_ssl(self): @@ -166,7 +167,7 @@ class CoreConfig(JsonConfig): @property def path_extern_addons_local(self): """Return path for custom Add-ons.""" - return PurePath(self.path_extern_hassio, ADDONS_LOCAL) + return PurePath(self.path_extern_supervisor, ADDONS_LOCAL) @property def path_addons_data(self): @@ -176,7 +177,7 @@ class CoreConfig(JsonConfig): @property def path_extern_addons_data(self): """Return root add-on data folder external for Docker.""" - return PurePath(self.path_extern_hassio, ADDONS_DATA) + return PurePath(self.path_extern_supervisor, ADDONS_DATA) @property def path_audio(self): @@ -186,7 +187,7 @@ class CoreConfig(JsonConfig): @property def path_extern_audio(self): """Return root audio data folder external for Docker.""" - return PurePath(self.path_extern_hassio, AUDIO_DATA) + return PurePath(self.path_extern_supervisor, AUDIO_DATA) @property def path_tmp(self): @@ -196,7 +197,7 @@ class CoreConfig(JsonConfig): @property def path_extern_tmp(self): """Return Supervisor temp folder for Docker.""" - return PurePath(self.path_extern_hassio, TMP_DATA) + return PurePath(self.path_extern_supervisor, TMP_DATA) @property def path_backup(self): @@ -206,7 +207,7 @@ class CoreConfig(JsonConfig): @property def path_extern_backup(self): """Return root backup data folder external for Docker.""" - return PurePath(self.path_extern_hassio, BACKUP_DATA) + return PurePath(self.path_extern_supervisor, BACKUP_DATA) @property def path_share(self): @@ -221,12 +222,12 @@ class CoreConfig(JsonConfig): @property def path_extern_share(self): """Return root share data folder external for Docker.""" - return PurePath(self.path_extern_hassio, SHARE_DATA) + return PurePath(self.path_extern_supervisor, SHARE_DATA) @property def path_extern_dns(self): """Return dns path external for Docker.""" - return str(PurePath(self.path_extern_hassio, DNS_DATA)) + return str(PurePath(self.path_extern_supervisor, DNS_DATA)) @property def path_dns(self): diff --git a/supervisor/const.py b/supervisor/const.py index c4067278f..ce64a018f 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -68,8 +68,14 @@ ENV_TOKEN_OLD = "HASSIO_TOKEN" ENV_TOKEN = "SUPERVISOR_TOKEN" ENV_TIME = "TZ" +ENV_HOMEASSISTANT_REPOSITORY = "HOMEASSISTANT_REPOSITORY" +ENV_SUPERVISOR_SHARE = "SUPERVISOR_SHARE" +ENV_SUPERVISOR_NAME = "SUPERVISOR_NAME" +ENV_SUPERVISOR_MACHINE = "SUPERVISOR_MACHINE" + REQUEST_FROM = "HASSIO_FROM" +ATTR_SUPERVISOR = "supervisor" ATTR_MACHINE = "machine" ATTR_WAIT_BOOT = "wait_boot" ATTR_DEPLOYMENT = "deployment" @@ -138,7 +144,6 @@ ATTR_USER = "user" ATTR_SYSTEM = "system" ATTR_SNAPSHOTS = "snapshots" ATTR_HOMEASSISTANT = "homeassistant" -ATTR_HASSIO = "hassio" ATTR_HASSIO_API = "hassio_api" ATTR_HOMEASSISTANT_API = "homeassistant_api" ATTR_UUID = "uuid" diff --git a/supervisor/core.py b/supervisor/core.py index 77d0ee5a1..e73938c45 100644 --- a/supervisor/core.py +++ b/supervisor/core.py @@ -203,10 +203,12 @@ class Core(CoreSysAttributes): _LOGGER.info("Start repairing of Supervisor Environment") await self.sys_run_in_executor(self.sys_docker.repair) + # Fix plugins + await asyncio.wait( + [self.sys_dns.repair(), self.sys_audio.repair(), self.sys_cli.repair()] + ) + # Restore core functionality - await self.sys_dns.repair() - await self.sys_audio.repair() - await self.sys_cli.repair() await self.sys_addons.repair() await self.sys_homeassistant.repair() diff --git a/supervisor/coresys.py b/supervisor/coresys.py index 6015df87c..510e6ff70 100644 --- a/supervisor/coresys.py +++ b/supervisor/coresys.py @@ -42,7 +42,8 @@ class CoreSys: def __init__(self): """Initialize coresys.""" # Static attributes - self.machine_id: Optional[str] = None + self._machine_id: Optional[str] = None + self._machine: Optional[str] = None # External objects self._loop: asyncio.BaseEventLoop = asyncio.get_running_loop() @@ -81,13 +82,6 @@ class CoreSys: self._discovery: Optional[Discovery] = None self._hwmonitor: Optional[HwMonitor] = None - @property - def machine(self) -> str: - """Return running machine type of the Supervisor system.""" - if self._homeassistant: - return self._homeassistant.machine - return None - @property def dev(self) -> bool: """Return True if we run dev mode.""" @@ -397,6 +391,30 @@ class CoreSys: raise RuntimeError("HassOS already set!") self._hassos = value + @property + def machine(self) -> Optional[str]: + """Return machine type string.""" + return self._machine + + @machine.setter + def machine(self, value: str): + """Set a machine type string.""" + if self._machine: + raise RuntimeError("Machine type already set!") + self._machine = value + + @property + def machine_id(self) -> Optional[str]: + """Return machine-id type string.""" + return self._machine_id + + @machine_id.setter + def machine_id(self, value: str): + """Set a machine-id type string.""" + if self._machine_id: + raise RuntimeError("Machine-ID type already set!") + self._machine_id = value + class CoreSysAttributes: """Inheret basic CoreSysAttributes.""" diff --git a/supervisor/dns.py b/supervisor/dns.py index 516fe2ed3..8cadde70a 100644 --- a/supervisor/dns.py +++ b/supervisor/dns.py @@ -167,14 +167,16 @@ class CoreDNS(JsonConfig, CoreSysAttributes): if self.latest_version: with suppress(DockerAPIError): - await self.instance.install(self.latest_version) + await self.instance.install( + self.latest_version, image=self.sys_updater.image_dns + ) break _LOGGER.warning("Error on install CoreDNS plugin. Retry in 30sec") await asyncio.sleep(30) _LOGGER.info("CoreDNS plugin now installed") self.version = self.instance.version - self.image = self.instance.image + self.image = self.sys_updater.image_dns self.save_data() # Init Hosts @@ -183,6 +185,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes): async def update(self, version: Optional[str] = None) -> None: """Update CoreDNS plugin.""" version = version or self.latest_version + old_image = self.image if version == self.version: _LOGGER.warning("Version %s is already installed for CoreDNS", version) @@ -190,17 +193,18 @@ class CoreDNS(JsonConfig, CoreSysAttributes): # Update try: - await self.instance.update(version) + await self.instance.update(version, image=self.sys_updater.image_dns) except DockerAPIError: _LOGGER.error("CoreDNS update fails") raise CoreDNSUpdateError() from None + else: + self.version = version + self.image = self.sys_updater.image_dns + self.save_data() # Cleanup with suppress(DockerAPIError): - await self.instance.cleanup() - - self.version = version - self.save_data() + await self.instance.cleanup(old_image=old_image) # Start CoreDNS await self.start() diff --git a/supervisor/docker/cli.py b/supervisor/docker/cli.py index 08866b46e..0f9fe6b27 100644 --- a/supervisor/docker/cli.py +++ b/supervisor/docker/cli.py @@ -60,5 +60,5 @@ class DockerCli(DockerInterface, CoreSysAttributes): "Start CLI %s with version %s - %s", self.image, self.version, - self.sys_docker.network.audio, + self.sys_docker.network.cli, ) diff --git a/supervisor/docker/interface.py b/supervisor/docker/interface.py index ba0c5c54a..8a6819d74 100644 --- a/supervisor/docker/interface.py +++ b/supervisor/docker/interface.py @@ -302,11 +302,11 @@ class DockerInterface(CoreSysAttributes): _LOGGER.warning("Can't grep logs from %s: %s", self.image, err) @process_lock - def cleanup(self) -> Awaitable[None]: + def cleanup(self, old_image: Optional[str] = None) -> Awaitable[None]: """Check if old version exists and cleanup.""" - return self.sys_run_in_executor(self._cleanup) + return self.sys_run_in_executor(self._cleanup, old_image) - def _cleanup(self) -> None: + def _cleanup(self, old_image: Optional[str] = None) -> None: """Check if old version exists and cleanup. Need run inside executor. @@ -317,6 +317,7 @@ class DockerInterface(CoreSysAttributes): _LOGGER.warning("Can't find %s for cleanup", self.image) raise DockerAPIError() from None + # Cleanup Current for image in self.sys_docker.images.list(name=self.image): if origin.id == image.id: continue @@ -325,6 +326,15 @@ class DockerInterface(CoreSysAttributes): _LOGGER.info("Cleanup images: %s", image.tags) self.sys_docker.images.remove(image.id, force=True) + # Cleanup Old + if not old_image or self.image == old_image: + return + + for image in self.sys_docker.images.list(name=old_image): + with suppress(docker.errors.DockerException): + _LOGGER.info("Cleanup images: %s", image.tags) + self.sys_docker.images.remove(image.id, force=True) + @process_lock def restart(self) -> Awaitable[None]: """Restart docker container.""" diff --git a/supervisor/homeassistant.py b/supervisor/homeassistant.py index 62bf9581e..c9fc68f77 100644 --- a/supervisor/homeassistant.py +++ b/supervisor/homeassistant.py @@ -4,7 +4,6 @@ from contextlib import asynccontextmanager, suppress from datetime import datetime, timedelta from ipaddress import IPv4Address import logging -import os from pathlib import Path import re import secrets @@ -24,7 +23,6 @@ from .const import ( ATTR_AUDIO_OUTPUT, ATTR_BOOT, ATTR_IMAGE, - ATTR_VERSION_LATEST, ATTR_PORT, ATTR_REFRESH_TOKEN, ATTR_SSL, @@ -166,20 +164,12 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): """Return last available version of Home Assistant.""" return self.sys_updater.version_homeassistant - @latest_version.setter - def latest_version(self, value: str): - """Set last available version of Home Assistant.""" - if value: - self._data[ATTR_VERSION_LATEST] = value - else: - self._data.pop(ATTR_VERSION_LATEST, None) - @property def image(self) -> str: """Return image name of the Home Assistant container.""" if self._data.get(ATTR_IMAGE): return self._data[ATTR_IMAGE] - return os.environ["HOMEASSISTANT_REPOSITORY"] + return f"homeassistant/{self.sys_machine}-homeassistant" @image.setter def image(self, value: str) -> None: @@ -262,12 +252,15 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): _LOGGER.info("Setup HomeAssistant landingpage") while True: try: - await self.instance.install("landingpage") + await self.instance.install( + "landingpage", image=self.sys_updater.image_homeassistant + ) except DockerAPIError: _LOGGER.warning("Fails install landingpage, retry after 30sec") await asyncio.sleep(30) else: self.version = self.instance.version + self.image = self.sys_updater.image_homeassistant self.save_data() break @@ -288,14 +281,16 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): tag = self.latest_version if tag: with suppress(DockerAPIError): - await self.instance.update(tag) + await self.instance.update( + tag, image=self.sys_updater.image_homeassistant + ) break _LOGGER.warning("Error on install Home Assistant. Retry in 30sec") await asyncio.sleep(30) _LOGGER.info("Home Assistant docker now installed") self.version = self.instance.version - self.image = self.instance.image + self.image = self.sys_updater.image_homeassistant self.save_data() # finishing @@ -313,6 +308,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): async def update(self, version: Optional[str] = None) -> None: """Update HomeAssistant version.""" version = version or self.latest_version + old_image = self.image rollback = self.version if not self.error_state else None running = await self.instance.is_running() exists = await self.instance.exists() @@ -326,20 +322,24 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): """Run Home Assistant update.""" _LOGGER.info("Update Home Assistant to version %s", to_version) try: - await self.instance.update(to_version) + await self.instance.update( + to_version, image=self.sys_updater.image_homeassistant + ) except DockerAPIError: _LOGGER.warning("Update Home Assistant image fails") raise HomeAssistantUpdateError() from None else: self.version = self.instance.version + self.image = self.sys_updater.image_homeassistant if running: await self._start() - _LOGGER.info("Successful run Home Assistant %s", to_version) + + # Successfull - last step self.save_data() with suppress(DockerAPIError): - await self.instance.cleanup() + await self.instance.cleanup(old_image=old_image) # Update Home Assistant with suppress(HomeAssistantError): diff --git a/supervisor/ingress.py b/supervisor/ingress.py index f6fad670f..1c7ef85e1 100644 --- a/supervisor/ingress.py +++ b/supervisor/ingress.py @@ -127,6 +127,11 @@ class Ingress(JsonConfig, CoreSysAttributes): async def update_hass_panel(self, addon: Addon): """Return True if Home Assistant up and running.""" + if not await self.sys_homeassistant.is_running(): + _LOGGER.debug("Ignore panel update on Core") + return + + # Update UI method = "post" if addon.ingress_panel else "delete" async with self.sys_homeassistant.make_request( method, f"api/hassio_push/panel/{addon.slug}" diff --git a/supervisor/snapshots/snapshot.py b/supervisor/snapshots/snapshot.py index bb74af991..abbdc3919 100644 --- a/supervisor/snapshots/snapshot.py +++ b/supervisor/snapshots/snapshot.py @@ -359,7 +359,7 @@ class Snapshot(CoreSysAttributes): """Internal function to snapshot a folder.""" slug_name = name.replace("/", "_") tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz") - origin_dir = Path(self.sys_config.path_hassio, name) + origin_dir = Path(self.sys_config.path_supervisor, name) # Check if exists if not origin_dir.is_dir(): @@ -396,7 +396,7 @@ class Snapshot(CoreSysAttributes): """Intenal function to restore a folder.""" slug_name = name.replace("/", "_") tar_name = Path(self._tmp.name, f"{slug_name}.tar.gz") - origin_dir = Path(self.sys_config.path_hassio, name) + origin_dir = Path(self.sys_config.path_supervisor, name) # Check if exists inside snapshot if not tar_name.exists(): diff --git a/supervisor/supervisor.py b/supervisor/supervisor.py index ee5be07b1..59f5ca426 100644 --- a/supervisor/supervisor.py +++ b/supervisor/supervisor.py @@ -65,7 +65,7 @@ class Supervisor(CoreSysAttributes): @property def latest_version(self) -> str: """Return last available version of Home Assistant.""" - return self.sys_updater.version_hassio + return self.sys_updater.version_supervisor @property def image(self) -> str: @@ -115,7 +115,9 @@ class Supervisor(CoreSysAttributes): _LOGGER.info("Update Supervisor to version %s", version) try: - await self.instance.install(version, image=None, latest=True) + await self.instance.install( + version, image=self.sys_updater.image_supervisor, latest=True + ) except DockerAPIError: _LOGGER.error("Update of Supervisor fails!") raise SupervisorUpdateError() from None diff --git a/supervisor/updater.py b/supervisor/updater.py index 47ac77001..cffe86e84 100644 --- a/supervisor/updater.py +++ b/supervisor/updater.py @@ -13,9 +13,10 @@ from .const import ( ATTR_CHANNEL, ATTR_CLI, ATTR_DNS, - ATTR_HASSIO, + ATTR_SUPERVISOR, ATTR_HASSOS, ATTR_HOMEASSISTANT, + ATTR_IMAGE, FILE_HASSIO_UPDATER, URL_HASSIO_VERSION, UpdateChannels, @@ -53,9 +54,9 @@ class Updater(JsonConfig, CoreSysAttributes): return self._data.get(ATTR_HOMEASSISTANT) @property - def version_hassio(self) -> Optional[str]: + def version_supervisor(self) -> Optional[str]: """Return latest version of Supervisor.""" - return self._data.get(ATTR_HASSIO) + return self._data.get(ATTR_SUPERVISOR) @property def version_hassos(self) -> Optional[str]: @@ -77,6 +78,51 @@ class Updater(JsonConfig, CoreSysAttributes): """Return latest version of Audio.""" return self._data.get(ATTR_AUDIO) + @property + def image_homeassistant(self) -> Optional[str]: + """Return latest version of Home Assistant.""" + return ( + self._data[ATTR_IMAGE] + .get(ATTR_HOMEASSISTANT, "") + .format(machine=self.sys_machine) + ) + + @property + def image_supervisor(self) -> Optional[str]: + """Return latest version of Supervisor.""" + return ( + self._data[ATTR_IMAGE] + .get(ATTR_SUPERVISOR, "") + .format(arch=self.sys_arch.supervisor) + ) + + @property + def image_cli(self) -> Optional[str]: + """Return latest version of CLI.""" + return ( + self._data[ATTR_IMAGE] + .get(ATTR_CLI, "") + .format(arch=self.sys_arch.supervisor) + ) + + @property + def image_dns(self) -> Optional[str]: + """Return latest version of DNS.""" + return ( + self._data[ATTR_IMAGE] + .get(ATTR_DNS, "") + .format(arch=self.sys_arch.supervisor) + ) + + @property + def image_audio(self) -> Optional[str]: + """Return latest version of Audio.""" + return ( + self._data[ATTR_IMAGE] + .get(ATTR_AUDIO, "") + .format(arch=self.sys_arch.supervisor) + ) + @property def channel(self) -> UpdateChannels: """Return upstream channel of Supervisor instance.""" @@ -116,7 +162,7 @@ class Updater(JsonConfig, CoreSysAttributes): try: # Update supervisor version - self._data[ATTR_HASSIO] = data["supervisor"] + self._data[ATTR_SUPERVISOR] = data["supervisor"] # Update Home Assistant core version self._data[ATTR_HOMEASSISTANT] = data["homeassistant"][machine] @@ -130,6 +176,13 @@ class Updater(JsonConfig, CoreSysAttributes): self._data[ATTR_DNS] = data["dns"] self._data[ATTR_AUDIO] = data["audio"] + # Update images for that versions + self._data[ATTR_IMAGE][ATTR_HOMEASSISTANT] = data["image"]["core"] + self._data[ATTR_IMAGE][ATTR_SUPERVISOR] = data["image"]["supervisor"] + self._data[ATTR_IMAGE][ATTR_AUDIO] = data["image"]["audio"] + self._data[ATTR_IMAGE][ATTR_CLI] = data["image"]["cli"] + self._data[ATTR_IMAGE][ATTR_DNS] = data["image"]["dns"] + except KeyError as err: _LOGGER.warning("Can't process version data: %s", err) raise HassioUpdaterError() from None diff --git a/supervisor/validate.py b/supervisor/validate.py index e7a28c199..ceb45a6ec 100644 --- a/supervisor/validate.py +++ b/supervisor/validate.py @@ -17,7 +17,7 @@ from .const import ( ATTR_DEBUG, ATTR_DEBUG_BLOCK, ATTR_DNS, - ATTR_HASSIO, + ATTR_SUPERVISOR, ATTR_HASSOS, ATTR_HOMEASSISTANT, ATTR_IMAGE, @@ -124,11 +124,21 @@ SCHEMA_UPDATER_CONFIG = vol.Schema( UpdateChannels ), vol.Optional(ATTR_HOMEASSISTANT): vol.Coerce(str), - vol.Optional(ATTR_HASSIO): vol.Coerce(str), + vol.Optional(ATTR_SUPERVISOR): vol.Coerce(str), vol.Optional(ATTR_HASSOS): vol.Coerce(str), vol.Optional(ATTR_CLI): vol.Coerce(str), vol.Optional(ATTR_DNS): vol.Coerce(str), vol.Optional(ATTR_AUDIO): vol.Coerce(str), + vol.Optional(ATTR_IMAGE, default=dict): vol.Schema( + { + vol.Optional(ATTR_HOMEASSISTANT): docker_image, + vol.Optional(ATTR_SUPERVISOR): docker_image, + vol.Optional(ATTR_CLI): docker_image, + vol.Optional(ATTR_DNS): docker_image, + vol.Optional(ATTR_AUDIO): docker_image, + }, + extra=vol.REMOVE_EXTRA, + ), }, extra=vol.REMOVE_EXTRA, ) From 69cea9fc9687ec07397e7dd08b9d611e6272af2c Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 5 Apr 2020 01:20:49 +0200 Subject: [PATCH 07/17] Plugin cleanup (#1632) * Plugin cleanup * Fix setup --- setup.py | 1 + supervisor/bootstrap.py | 6 +++--- supervisor/coresys.py | 6 +++--- supervisor/plugins/__init__.py | 1 + supervisor/{ => plugins}/audio.py | 14 +++++++------- supervisor/{ => plugins}/cli.py | 14 +++++++------- supervisor/{ => plugins}/dns.py | 16 ++++++++-------- 7 files changed, 30 insertions(+), 28 deletions(-) create mode 100644 supervisor/plugins/__init__.py rename supervisor/{ => plugins}/audio.py (95%) rename supervisor/{ => plugins}/cli.py (94%) rename supervisor/{ => plugins}/dns.py (96%) diff --git a/setup.py b/setup.py index 85159ee8e..200196eea 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ setup( "supervisor.api", "supervisor.misc", "supervisor.utils", + "supervisor.plugins", "supervisor.snapshots", ], include_package_data=True, diff --git a/supervisor/bootstrap.py b/supervisor/bootstrap.py index 6c23e68e2..2e49ff451 100644 --- a/supervisor/bootstrap.py +++ b/supervisor/bootstrap.py @@ -11,7 +11,6 @@ from .addons import AddonManager from .api import RestAPI from .arch import CpuArch from .auth import Auth -from .audio import Audio from .const import ( SOCKET_DOCKER, UpdateChannels, @@ -21,11 +20,9 @@ from .const import ( ENV_SUPERVISOR_MACHINE, ) from .core import Core -from .cli import HaCli from .coresys import CoreSys from .dbus import DBusManager from .discovery import Discovery -from .dns import CoreDNS from .hassos import HassOS from .homeassistant import HomeAssistant from .host import HostManager @@ -39,6 +36,9 @@ from .tasks import Tasks from .updater import Updater from .secrets import SecretsManager from .utils.dt import fetch_timezone +from .plugins.dns import CoreDNS +from .plugins.cli import HaCli +from .plugins.audio import Audio _LOGGER: logging.Logger = logging.getLogger(__name__) diff --git a/supervisor/coresys.py b/supervisor/coresys.py index 510e6ff70..b80c069e5 100644 --- a/supervisor/coresys.py +++ b/supervisor/coresys.py @@ -15,13 +15,10 @@ if TYPE_CHECKING: from .addons import AddonManager from .api import RestAPI from .arch import CpuArch - from .audio import Audio from .auth import Auth from .core import Core - from .cli import HaCli from .dbus import DBusManager from .discovery import Discovery - from .dns import CoreDNS from .hassos import HassOS from .hwmon import HwMonitor from .homeassistant import HomeAssistant @@ -34,6 +31,9 @@ if TYPE_CHECKING: from .store import StoreManager from .tasks import Tasks from .updater import Updater + from .plugins.cli import HaCli + from .plugins.audio import Audio + from .plugins.dns import CoreDNS class CoreSys: diff --git a/supervisor/plugins/__init__.py b/supervisor/plugins/__init__.py new file mode 100644 index 000000000..d2cffc4d9 --- /dev/null +++ b/supervisor/plugins/__init__.py @@ -0,0 +1 @@ +"""Plugin for Supervisor backend.""" diff --git a/supervisor/audio.py b/supervisor/plugins/audio.py similarity index 95% rename from supervisor/audio.py rename to supervisor/plugins/audio.py index d98c740d4..1cd68a099 100644 --- a/supervisor/audio.py +++ b/supervisor/plugins/audio.py @@ -8,13 +8,13 @@ from typing import Awaitable, Optional import jinja2 -from .const import ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_AUDIO -from .coresys import CoreSys, CoreSysAttributes -from .docker.audio import DockerAudio -from .docker.stats import DockerStats -from .exceptions import AudioError, AudioUpdateError, DockerAPIError -from .utils.json import JsonConfig -from .validate import SCHEMA_AUDIO_CONFIG +from ..const import ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_AUDIO +from ..coresys import CoreSys, CoreSysAttributes +from ..docker.audio import DockerAudio +from ..docker.stats import DockerStats +from ..exceptions import AudioError, AudioUpdateError, DockerAPIError +from ..utils.json import JsonConfig +from ..validate import SCHEMA_AUDIO_CONFIG _LOGGER: logging.Logger = logging.getLogger(__name__) diff --git a/supervisor/cli.py b/supervisor/plugins/cli.py similarity index 94% rename from supervisor/cli.py rename to supervisor/plugins/cli.py index af7171166..4d7e1fdc7 100644 --- a/supervisor/cli.py +++ b/supervisor/plugins/cli.py @@ -5,13 +5,13 @@ import logging import secrets from typing import Awaitable, Optional -from .const import ATTR_ACCESS_TOKEN, ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_CLI -from .coresys import CoreSys, CoreSysAttributes -from .docker.cli import DockerCli -from .docker.stats import DockerStats -from .exceptions import CliError, CliUpdateError, DockerAPIError -from .utils.json import JsonConfig -from .validate import SCHEMA_CLI_CONFIG +from ..const import ATTR_ACCESS_TOKEN, ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_CLI +from ..coresys import CoreSys, CoreSysAttributes +from ..docker.cli import DockerCli +from ..docker.stats import DockerStats +from ..exceptions import CliError, CliUpdateError, DockerAPIError +from ..utils.json import JsonConfig +from ..validate import SCHEMA_CLI_CONFIG _LOGGER: logging.Logger = logging.getLogger(__name__) diff --git a/supervisor/dns.py b/supervisor/plugins/dns.py similarity index 96% rename from supervisor/dns.py rename to supervisor/plugins/dns.py index 8cadde70a..c6e9ec30b 100644 --- a/supervisor/dns.py +++ b/supervisor/plugins/dns.py @@ -10,14 +10,14 @@ import attr import jinja2 import voluptuous as vol -from .const import ATTR_IMAGE, ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, FILE_HASSIO_DNS -from .coresys import CoreSys, CoreSysAttributes -from .docker.dns import DockerDNS -from .docker.stats import DockerStats -from .exceptions import CoreDNSError, CoreDNSUpdateError, DockerAPIError -from .misc.forwarder import DNSForward -from .utils.json import JsonConfig -from .validate import SCHEMA_DNS_CONFIG, dns_url +from ..const import ATTR_IMAGE, ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, FILE_HASSIO_DNS +from ..coresys import CoreSys, CoreSysAttributes +from ..docker.dns import DockerDNS +from ..docker.stats import DockerStats +from ..exceptions import CoreDNSError, CoreDNSUpdateError, DockerAPIError +from ..misc.forwarder import DNSForward +from ..utils.json import JsonConfig +from ..validate import SCHEMA_DNS_CONFIG, dns_url _LOGGER: logging.Logger = logging.getLogger(__name__) From cc56944d75634a734fc714fce3dcc832ae8d3a9a Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 5 Apr 2020 16:23:52 +0200 Subject: [PATCH 08/17] relicense project --- LICENSE | 811 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 642 insertions(+), 169 deletions(-) diff --git a/LICENSE b/LICENSE index ff6f65d61..f288702d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,674 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. - 1. Definitions. + Preamble - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + The precise terms and conditions for copying, distribution and +modification follow. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. + TERMS AND CONDITIONS - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. + 0. Definitions. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: + "This License" refers to version 3 of the GNU General Public License. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. + A "covered work" means either the unmodified Program or a work based +on the Program. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. + 1. Source Code. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. - END OF TERMS AND CONDITIONS + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. - APPENDIX: How to apply the Apache License to your work. + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. - Copyright 2017 Pascal Vizeli + The Corresponding Source for a work in source code form is that +same work. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + 2. Basic Permissions. - http://www.apache.org/licenses/LICENSE-2.0 + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 2364e1e652d79b68036bfe8b82e26bd24ce15fcf Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 5 Apr 2020 19:16:12 +0200 Subject: [PATCH 09/17] Remove old authentication (#1635) --- supervisor/api/proxy.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/supervisor/api/proxy.py b/supervisor/api/proxy.py index f7f6d74f8..4a285722a 100644 --- a/supervisor/api/proxy.py +++ b/supervisor/api/proxy.py @@ -123,21 +123,11 @@ class APIProxy(CoreSysAttributes): _LOGGER.error("Got unexpected response from HA WebSocket: %s", data) raise APIError() - if self.sys_homeassistant.refresh_token: - await self.sys_homeassistant.ensure_access_token() - await client.send_json( - { - "type": "auth", - "access_token": self.sys_homeassistant.access_token, - } - ) - else: - await client.send_json( - { - "type": "auth", - "api_password": self.sys_homeassistant.api_password, - } - ) + # Auth session + await self.sys_homeassistant.ensure_access_token() + await client.send_json( + {"type": "auth", "access_token": self.sys_homeassistant.access_token} + ) data = await client.receive_json() From f0ed2eba2bfbe1b735f4c5c0c45992ef8726273e Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sun, 5 Apr 2020 23:26:22 +0200 Subject: [PATCH 10/17] Multicast support on Hass.io Network (#1634) * Add multicast layer to docker * support network forward * add pluginmanager * finish multicast plugin * fix lint * Add shutdown for plugins * Add API * Add watchdog * Fix black * Fix path --- API.md | 36 +++++ supervisor/addons/__init__.py | 4 +- supervisor/addons/addon.py | 2 +- supervisor/api/__init__.py | 16 +++ supervisor/api/audio.py | 16 +-- supervisor/api/cli.py | 10 +- supervisor/api/dns.py | 26 ++-- supervisor/api/multicast.py | 76 +++++++++++ supervisor/api/security.py | 2 +- supervisor/bootstrap.py | 8 +- supervisor/const.py | 3 + supervisor/core.py | 13 +- supervisor/coresys.py | 66 ++------- supervisor/docker/addon.py | 8 +- supervisor/docker/audio.py | 4 +- supervisor/docker/cli.py | 6 +- supervisor/docker/dns.py | 4 +- supervisor/docker/homeassistant.py | 4 +- supervisor/docker/multicast.py | 59 ++++++++ supervisor/exceptions.py | 13 +- supervisor/homeassistant.py | 2 +- supervisor/plugins/__init__.py | 75 +++++++++++ supervisor/plugins/audio.py | 21 ++- supervisor/plugins/cli.py | 16 ++- supervisor/plugins/dns.py | 27 +++- supervisor/plugins/multicast.py | 208 +++++++++++++++++++++++++++++ supervisor/plugins/validate.py | 44 ++++++ supervisor/tasks.py | 76 ++++++++--- supervisor/updater.py | 21 ++- supervisor/validate.py | 35 +---- 30 files changed, 730 insertions(+), 171 deletions(-) create mode 100644 supervisor/api/multicast.py create mode 100644 supervisor/docker/multicast.py create mode 100644 supervisor/plugins/multicast.py create mode 100644 supervisor/plugins/validate.py diff --git a/API.md b/API.md index 305417f35..252ab59fc 100644 --- a/API.md +++ b/API.md @@ -935,6 +935,42 @@ return: } ``` +### Multicast + +- GET `/multicast/info` + +```json +{ + "version": "1", + "version_latest": "2" +} +``` + +- POST `/multicast/update` + +```json +{ + "version": "VERSION" +} +``` + +- POST `/multicast/restart` + +- GET `/multicast/stats` + +```json +{ + "cpu_percent": 0.0, + "memory_usage": 283123, + "memory_limit": 329392, + "memory_percent": 1.4, + "network_tx": 0, + "network_rx": 0, + "blk_read": 0, + "blk_write": 0 +} +``` + ### Auth / SSO API You can use the user system on homeassistant. We handle this auth system on diff --git a/supervisor/addons/__init__.py b/supervisor/addons/__init__.py index 865f66b8e..3d33a7c7c 100644 --- a/supervisor/addons/__init__.py +++ b/supervisor/addons/__init__.py @@ -325,10 +325,10 @@ class AddonManager(CoreSysAttributes): for addon in self.installed: if not await addon.instance.is_running(): continue - self.sys_dns.add_host( + self.sys_plugins.dns.add_host( ipv4=addon.ip_address, names=[addon.hostname], write=False ) # Write hosts files with suppress(CoreDNSError): - self.sys_dns.write_hosts() + self.sys_plugins.dns.write_hosts() diff --git a/supervisor/addons/addon.py b/supervisor/addons/addon.py index 38c6b4ad4..1342831f8 100644 --- a/supervisor/addons/addon.py +++ b/supervisor/addons/addon.py @@ -389,7 +389,7 @@ class Addon(AddonModel): def write_pulse(self): """Write asound config to file and return True on success.""" - pulse_config = self.sys_audio.pulse_client( + pulse_config = self.sys_plugins.audio.pulse_client( input_profile=self.audio_input, output_profile=self.audio_output ) diff --git a/supervisor/api/__init__.py b/supervisor/api/__init__.py index e405f88e6..2d0643a9c 100644 --- a/supervisor/api/__init__.py +++ b/supervisor/api/__init__.py @@ -23,6 +23,7 @@ from .security import SecurityMiddleware from .services import APIServices from .snapshots import APISnapshots from .supervisor import APISupervisor +from .multicast import APIMulticast _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -52,6 +53,7 @@ class RestAPI(CoreSysAttributes): self._register_host() self._register_os() self._register_cli() + self._register_multicast() self._register_hardware() self._register_homeassistant() self._register_proxy() @@ -113,6 +115,20 @@ class RestAPI(CoreSysAttributes): ] ) + def _register_multicast(self) -> None: + """Register Multicast functions.""" + api_multicast = APIMulticast() + api_multicast.coresys = self.coresys + + self.webapp.add_routes( + [ + web.get("/multicast/info", api_multicast.info), + web.get("/multicast/stats", api_multicast.stats), + web.post("/multicast/update", api_multicast.update), + web.post("/multicast/restart", api_multicast.restart), + ] + ) + def _register_hardware(self) -> None: """Register hardware functions.""" api_hardware = APIHardware() diff --git a/supervisor/api/audio.py b/supervisor/api/audio.py index 7a1cada11..c8e66d60a 100644 --- a/supervisor/api/audio.py +++ b/supervisor/api/audio.py @@ -68,8 +68,8 @@ class APIAudio(CoreSysAttributes): async def info(self, request: web.Request) -> Dict[str, Any]: """Return Audio information.""" return { - ATTR_VERSION: self.sys_audio.version, - ATTR_VERSION_LATEST: self.sys_audio.latest_version, + ATTR_VERSION: self.sys_plugins.audio.version, + ATTR_VERSION_LATEST: self.sys_plugins.audio.latest_version, ATTR_HOST: str(self.sys_docker.network.audio), ATTR_AUDIO: { ATTR_CARD: [attr.asdict(card) for card in self.sys_host.sound.cards], @@ -88,7 +88,7 @@ class APIAudio(CoreSysAttributes): @api_process async def stats(self, request: web.Request) -> Dict[str, Any]: """Return resource information.""" - stats = await self.sys_audio.stats() + stats = await self.sys_plugins.audio.stats() return { ATTR_CPU_PERCENT: stats.cpu_percent, @@ -105,21 +105,21 @@ class APIAudio(CoreSysAttributes): async def update(self, request: web.Request) -> None: """Update Audio plugin.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self.sys_audio.latest_version) + version = body.get(ATTR_VERSION, self.sys_plugins.audio.latest_version) - if version == self.sys_audio.version: + if version == self.sys_plugins.audio.version: raise APIError("Version {} is already in use".format(version)) - await asyncio.shield(self.sys_audio.update(version)) + await asyncio.shield(self.sys_plugins.audio.update(version)) @api_process_raw(CONTENT_TYPE_BINARY) def logs(self, request: web.Request) -> Awaitable[bytes]: """Return Audio Docker logs.""" - return self.sys_audio.logs() + return self.sys_plugins.audio.logs() @api_process def restart(self, request: web.Request) -> Awaitable[None]: """Restart Audio plugin.""" - return asyncio.shield(self.sys_audio.restart()) + return asyncio.shield(self.sys_plugins.audio.restart()) @api_process def reload(self, request: web.Request) -> Awaitable[None]: diff --git a/supervisor/api/cli.py b/supervisor/api/cli.py index 34cf13545..5a1aba8d5 100644 --- a/supervisor/api/cli.py +++ b/supervisor/api/cli.py @@ -33,14 +33,14 @@ class APICli(CoreSysAttributes): async def info(self, request: web.Request) -> Dict[str, Any]: """Return HA cli information.""" return { - ATTR_VERSION: self.sys_cli.version, - ATTR_VERSION_LATEST: self.sys_cli.latest_version, + ATTR_VERSION: self.sys_plugins.cli.version, + ATTR_VERSION_LATEST: self.sys_plugins.cli.latest_version, } @api_process async def stats(self, request: web.Request) -> Dict[str, Any]: """Return resource information.""" - stats = await self.sys_cli.stats() + stats = await self.sys_plugins.cli.stats() return { ATTR_CPU_PERCENT: stats.cpu_percent, @@ -57,6 +57,6 @@ class APICli(CoreSysAttributes): async def update(self, request: web.Request) -> None: """Update HA CLI.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self.sys_cli.latest_version) + version = body.get(ATTR_VERSION, self.sys_plugins.cli.latest_version) - await asyncio.shield(self.sys_cli.update(version)) + await asyncio.shield(self.sys_plugins.cli.update(version)) diff --git a/supervisor/api/dns.py b/supervisor/api/dns.py index f834b4327..80e7a9c59 100644 --- a/supervisor/api/dns.py +++ b/supervisor/api/dns.py @@ -42,10 +42,10 @@ class APICoreDNS(CoreSysAttributes): async def info(self, request: web.Request) -> Dict[str, Any]: """Return DNS information.""" return { - ATTR_VERSION: self.sys_dns.version, - ATTR_VERSION_LATEST: self.sys_dns.latest_version, + ATTR_VERSION: self.sys_plugins.dns.version, + ATTR_VERSION_LATEST: self.sys_plugins.dns.latest_version, ATTR_HOST: str(self.sys_docker.network.dns), - ATTR_SERVERS: self.sys_dns.servers, + ATTR_SERVERS: self.sys_plugins.dns.servers, ATTR_LOCALS: self.sys_host.network.dns_servers, } @@ -55,15 +55,15 @@ class APICoreDNS(CoreSysAttributes): body = await api_validate(SCHEMA_OPTIONS, request) if ATTR_SERVERS in body: - self.sys_dns.servers = body[ATTR_SERVERS] - self.sys_create_task(self.sys_dns.restart()) + self.sys_plugins.dns.servers = body[ATTR_SERVERS] + self.sys_create_task(self.sys_plugins.dns.restart()) - self.sys_dns.save_data() + self.sys_plugins.dns.save_data() @api_process async def stats(self, request: web.Request) -> Dict[str, Any]: """Return resource information.""" - stats = await self.sys_dns.stats() + stats = await self.sys_plugins.dns.stats() return { ATTR_CPU_PERCENT: stats.cpu_percent, @@ -80,23 +80,23 @@ class APICoreDNS(CoreSysAttributes): async def update(self, request: web.Request) -> None: """Update DNS plugin.""" body = await api_validate(SCHEMA_VERSION, request) - version = body.get(ATTR_VERSION, self.sys_dns.latest_version) + version = body.get(ATTR_VERSION, self.sys_plugins.dns.latest_version) - if version == self.sys_dns.version: + if version == self.sys_plugins.dns.version: raise APIError("Version {} is already in use".format(version)) - await asyncio.shield(self.sys_dns.update(version)) + await asyncio.shield(self.sys_plugins.dns.update(version)) @api_process_raw(CONTENT_TYPE_BINARY) def logs(self, request: web.Request) -> Awaitable[bytes]: """Return DNS Docker logs.""" - return self.sys_dns.logs() + return self.sys_plugins.dns.logs() @api_process def restart(self, request: web.Request) -> Awaitable[None]: """Restart CoreDNS plugin.""" - return asyncio.shield(self.sys_dns.restart()) + return asyncio.shield(self.sys_plugins.dns.restart()) @api_process def reset(self, request: web.Request) -> Awaitable[None]: """Reset CoreDNS plugin.""" - return asyncio.shield(self.sys_dns.reset()) + return asyncio.shield(self.sys_plugins.dns.reset()) diff --git a/supervisor/api/multicast.py b/supervisor/api/multicast.py new file mode 100644 index 000000000..1b3c33b28 --- /dev/null +++ b/supervisor/api/multicast.py @@ -0,0 +1,76 @@ +"""Init file for Supervisor Multicast RESTful API.""" +import asyncio +import logging +from typing import Any, Awaitable, Dict + +from aiohttp import web +import voluptuous as vol + +from ..const import ( + ATTR_BLK_READ, + ATTR_BLK_WRITE, + ATTR_CPU_PERCENT, + ATTR_VERSION_LATEST, + ATTR_MEMORY_LIMIT, + ATTR_MEMORY_PERCENT, + ATTR_MEMORY_USAGE, + ATTR_NETWORK_RX, + ATTR_NETWORK_TX, + ATTR_VERSION, + CONTENT_TYPE_BINARY, +) +from ..coresys import CoreSysAttributes +from ..exceptions import APIError +from .utils import api_process, api_process_raw, api_validate + +_LOGGER: logging.Logger = logging.getLogger(__name__) + +SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)}) + + +class APIMulticast(CoreSysAttributes): + """Handle RESTful API for Multicast functions.""" + + @api_process + async def info(self, request: web.Request) -> Dict[str, Any]: + """Return Multicast information.""" + return { + ATTR_VERSION: self.sys_plugins.multicast.version, + ATTR_VERSION_LATEST: self.sys_plugins.multicast.latest_version, + } + + @api_process + async def stats(self, request: web.Request) -> Dict[str, Any]: + """Return resource information.""" + stats = await self.sys_plugins.multicast.stats() + + return { + ATTR_CPU_PERCENT: stats.cpu_percent, + ATTR_MEMORY_USAGE: stats.memory_usage, + ATTR_MEMORY_LIMIT: stats.memory_limit, + ATTR_MEMORY_PERCENT: stats.memory_percent, + ATTR_NETWORK_RX: stats.network_rx, + ATTR_NETWORK_TX: stats.network_tx, + ATTR_BLK_READ: stats.blk_read, + ATTR_BLK_WRITE: stats.blk_write, + } + + @api_process + async def update(self, request: web.Request) -> None: + """Update Multicast plugin.""" + body = await api_validate(SCHEMA_VERSION, request) + version = body.get(ATTR_VERSION, self.sys_plugins.multicast.latest_version) + + if version == self.sys_plugins.multicast.version: + raise APIError("Version {} is already in use".format(version)) + await asyncio.shield(self.sys_plugins.multicast.update(version)) + + @api_process_raw(CONTENT_TYPE_BINARY) + def logs(self, request: web.Request) -> Awaitable[bytes]: + """Return Multicast Docker logs.""" + return self.sys_plugins.multicast.logs() + + @api_process + def restart(self, request: web.Request) -> Awaitable[None]: + """Restart Multicast plugin.""" + return asyncio.shield(self.sys_plugins.multicast.restart()) diff --git a/supervisor/api/security.py b/supervisor/api/security.py index 663c4b062..559104c43 100644 --- a/supervisor/api/security.py +++ b/supervisor/api/security.py @@ -130,7 +130,7 @@ class SecurityMiddleware(CoreSysAttributes): # Host # Remove machine_id handling later if all use new CLI - if supervisor_token in (self.sys_machine_id, self.sys_cli.supervisor_token): + if supervisor_token in (self.sys_machine_id, self.sys_plugins.cli.supervisor_token): _LOGGER.debug("%s access from Host", request.path) request_from = self.sys_host diff --git a/supervisor/bootstrap.py b/supervisor/bootstrap.py index 2e49ff451..932180ca4 100644 --- a/supervisor/bootstrap.py +++ b/supervisor/bootstrap.py @@ -35,10 +35,8 @@ from .supervisor import Supervisor from .tasks import Tasks from .updater import Updater from .secrets import SecretsManager +from .plugins import PluginManager from .utils.dt import fetch_timezone -from .plugins.dns import CoreDNS -from .plugins.cli import HaCli -from .plugins.audio import Audio _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -52,9 +50,8 @@ async def initialize_coresys(): # Initialize core objects coresys.core = Core(coresys) - coresys.dns = CoreDNS(coresys) + coresys.plugins = PluginManager(coresys) coresys.arch = CpuArch(coresys) - coresys.audio = Audio(coresys) coresys.auth = Auth(coresys) coresys.updater = Updater(coresys) coresys.api = RestAPI(coresys) @@ -72,7 +69,6 @@ async def initialize_coresys(): coresys.dbus = DBusManager(coresys) coresys.hassos = HassOS(coresys) coresys.secrets = SecretsManager(coresys) - coresys.cli = HaCli(coresys) # bootstrap config initialize_system_data(coresys) diff --git a/supervisor/const.py b/supervisor/const.py index ce64a018f..7bb025e8e 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -28,6 +28,7 @@ FILE_HASSIO_INGRESS = Path(SUPERVISOR_DATA, "ingress.json") FILE_HASSIO_DNS = Path(SUPERVISOR_DATA, "dns.json") FILE_HASSIO_AUDIO = Path(SUPERVISOR_DATA, "audio.json") FILE_HASSIO_CLI = Path(SUPERVISOR_DATA, "cli.json") +FILE_HASSIO_MULTICAST = Path(SUPERVISOR_DATA, "multicast.json") SOCKET_DOCKER = Path("/run/docker.sock") @@ -67,6 +68,7 @@ HEADER_TOKEN_OLD = "X-Hassio-Key" ENV_TOKEN_OLD = "HASSIO_TOKEN" ENV_TOKEN = "SUPERVISOR_TOKEN" ENV_TIME = "TZ" +ENV_HASSIO_NETWORK = "HASSIO_NETWORK" ENV_HOMEASSISTANT_REPOSITORY = "HOMEASSISTANT_REPOSITORY" ENV_SUPERVISOR_SHARE = "SUPERVISOR_SHARE" @@ -77,6 +79,7 @@ REQUEST_FROM = "HASSIO_FROM" ATTR_SUPERVISOR = "supervisor" ATTR_MACHINE = "machine" +ATTR_MULTICAST = "multicast" ATTR_WAIT_BOOT = "wait_boot" ATTR_DEPLOYMENT = "deployment" ATTR_WATCHDOG = "watchdog" diff --git a/supervisor/core.py b/supervisor/core.py index e73938c45..a2f22cfd6 100644 --- a/supervisor/core.py +++ b/supervisor/core.py @@ -41,9 +41,7 @@ class Core(CoreSysAttributes): await self.sys_host.load() # Load Plugins container - await asyncio.wait( - [self.sys_dns.load(), self.sys_audio.load(), self.sys_cli.load()] - ) + await self.sys_plugins.load() # Load Home Assistant await self.sys_homeassistant.load() @@ -172,7 +170,7 @@ class Core(CoreSysAttributes): self.sys_websession.close(), self.sys_websession_ssl.close(), self.sys_ingress.unload(), - self.sys_dns.unload(), + self.sys_plugins.unload(), self.sys_hwmonitor.unload(), ] ) @@ -193,6 +191,9 @@ class Core(CoreSysAttributes): await self.sys_addons.shutdown(STARTUP_SYSTEM) await self.sys_addons.shutdown(STARTUP_INITIALIZE) + # Shutdown all Plugins + await self.sys_plugins.shutdown() + def _update_last_boot(self): """Update last boot time.""" self.sys_config.last_boot = self.sys_hardware.last_boot @@ -204,9 +205,7 @@ class Core(CoreSysAttributes): await self.sys_run_in_executor(self.sys_docker.repair) # Fix plugins - await asyncio.wait( - [self.sys_dns.repair(), self.sys_audio.repair(), self.sys_cli.repair()] - ) + await self.sys_plugins.repair() # Restore core functionality await self.sys_addons.repair() diff --git a/supervisor/coresys.py b/supervisor/coresys.py index b80c069e5..f04c200fb 100644 --- a/supervisor/coresys.py +++ b/supervisor/coresys.py @@ -31,9 +31,7 @@ if TYPE_CHECKING: from .store import StoreManager from .tasks import Tasks from .updater import Updater - from .plugins.cli import HaCli - from .plugins.audio import Audio - from .plugins.dns import CoreDNS + from .plugins import PluginManager class CoreSys: @@ -61,10 +59,7 @@ class CoreSys: # Internal objects pointers self._core: Optional[Core] = None self._arch: Optional[CpuArch] = None - self._audio: Optional[Audio] = None self._auth: Optional[Auth] = None - self._dns: Optional[CoreDNS] = None - self._cli: Optional[HaCli] = None self._homeassistant: Optional[HomeAssistant] = None self._supervisor: Optional[Supervisor] = None self._addons: Optional[AddonManager] = None @@ -81,6 +76,7 @@ class CoreSys: self._store: Optional[StoreManager] = None self._discovery: Optional[Discovery] = None self._hwmonitor: Optional[HwMonitor] = None + self._plugins: Optional[PluginManager] = None @property def dev(self) -> bool: @@ -140,16 +136,16 @@ class CoreSys: self._core = value @property - def cli(self) -> HaCli: - """Return HaCli object.""" - return self._cli + def plugins(self) -> PluginManager: + """Return PluginManager object.""" + return self._plugins - @cli.setter - def cli(self, value: HaCli): - """Set a HaCli object.""" - if self._cli: - raise RuntimeError("HaCli already set!") - self._cli = value + @plugins.setter + def plugins(self, value: PluginManager): + """Set a PluginManager object.""" + if self._plugins: + raise RuntimeError("PluginManager already set!") + self._plugins = value @property def arch(self) -> CpuArch: @@ -175,18 +171,6 @@ class CoreSys: raise RuntimeError("Auth already set!") self._auth = value - @property - def audio(self) -> Audio: - """Return Audio object.""" - return self._audio - - @audio.setter - def audio(self, value: Audio): - """Set a Audio object.""" - if self._audio: - raise RuntimeError("Audio already set!") - self._audio = value - @property def homeassistant(self) -> HomeAssistant: """Return Home Assistant object.""" @@ -331,18 +315,6 @@ class CoreSys: raise RuntimeError("DBusManager already set!") self._dbus = value - @property - def dns(self) -> CoreDNS: - """Return CoreDNS object.""" - return self._dns - - @dns.setter - def dns(self, value: CoreDNS): - """Set a CoreDNS object.""" - if self._dns: - raise RuntimeError("CoreDNS already set!") - self._dns = value - @property def host(self) -> HostManager: """Return HostManager object.""" @@ -482,9 +454,9 @@ class CoreSysAttributes: return self.coresys.core @property - def sys_cli(self) -> HaCli: - """Return HaCli object.""" - return self.coresys.cli + def sys_plugins(self) -> PluginManager: + """Return PluginManager object.""" + return self.coresys.plugins @property def sys_arch(self) -> CpuArch: @@ -496,11 +468,6 @@ class CoreSysAttributes: """Return Auth object.""" return self.coresys.auth - @property - def sys_audio(self) -> Audio: - """Return Audio object.""" - return self.coresys.audio - @property def sys_homeassistant(self) -> HomeAssistant: """Return Home Assistant object.""" @@ -561,11 +528,6 @@ class CoreSysAttributes: """Return DBusManager object.""" return self.coresys.dbus - @property - def sys_dns(self) -> CoreDNS: - """Return CoreDNS object.""" - return self.coresys.dns - @property def sys_host(self) -> HostManager: """Return HostManager object.""" diff --git a/supervisor/docker/addon.py b/supervisor/docker/addon.py index 05dd420a3..f52b26332 100644 --- a/supervisor/docker/addon.py +++ b/supervisor/docker/addon.py @@ -308,11 +308,11 @@ class DockerAddon(DockerInterface): "bind": "/etc/pulse/client.conf", "mode": "ro", }, - str(self.sys_audio.path_extern_pulse): { + str(self.sys_plugins.audio.path_extern_pulse): { "bind": "/run/audio", "mode": "ro", }, - str(self.sys_audio.path_extern_asound): { + str(self.sys_plugins.audio.path_extern_asound): { "bind": "/etc/asound.conf", "mode": "ro", }, @@ -364,7 +364,7 @@ class DockerAddon(DockerInterface): _LOGGER.info("Start Docker add-on %s with version %s", self.image, self.version) # Write data to DNS server - self.sys_dns.add_host(ipv4=self.ip_address, names=[self.addon.hostname]) + self.sys_plugins.dns.add_host(ipv4=self.ip_address, names=[self.addon.hostname]) def _install( self, tag: str, image: Optional[str] = None, latest: bool = False @@ -490,5 +490,5 @@ class DockerAddon(DockerInterface): Need run inside executor. """ if self.ip_address != NO_ADDDRESS: - self.sys_dns.delete_host(self.addon.hostname) + self.sys_plugins.dns.delete_host(self.addon.hostname) super()._stop(remove_container) diff --git a/supervisor/docker/audio.py b/supervisor/docker/audio.py index efa93099f..01c037c5a 100644 --- a/supervisor/docker/audio.py +++ b/supervisor/docker/audio.py @@ -20,7 +20,7 @@ class DockerAudio(DockerInterface, CoreSysAttributes): @property def image(self) -> str: """Return name of Supervisor Audio image.""" - return self.sys_audio.image + return self.sys_plugins.audio.image @property def name(self) -> str: @@ -58,7 +58,7 @@ class DockerAudio(DockerInterface, CoreSysAttributes): # Create & Run container docker_container = self.sys_docker.run( self.image, - version=self.sys_audio.version, + version=self.sys_plugins.audio.version, init=False, ipv4=self.sys_docker.network.audio, name=self.name, diff --git a/supervisor/docker/cli.py b/supervisor/docker/cli.py index 0f9fe6b27..a7b2ba3a5 100644 --- a/supervisor/docker/cli.py +++ b/supervisor/docker/cli.py @@ -18,7 +18,7 @@ class DockerCli(DockerInterface, CoreSysAttributes): @property def image(self): """Return name of HA cli image.""" - return self.sys_cli.image + return self.sys_plugins.cli.image @property def name(self) -> str: @@ -42,7 +42,7 @@ class DockerCli(DockerInterface, CoreSysAttributes): self.image, entrypoint=["/init"], command=["/bin/bash", "-c", "sleep infinity"], - version=self.sys_cli.version, + version=self.sys_plugins.cli.version, init=False, ipv4=self.sys_docker.network.cli, name=self.name, @@ -51,7 +51,7 @@ class DockerCli(DockerInterface, CoreSysAttributes): extra_hosts={"supervisor": self.sys_docker.network.supervisor}, environment={ ENV_TIME: self.sys_timezone, - ENV_TOKEN: self.sys_cli.supervisor_token, + ENV_TOKEN: self.sys_plugins.cli.supervisor_token, }, ) diff --git a/supervisor/docker/dns.py b/supervisor/docker/dns.py index a20068035..94f87eb1b 100644 --- a/supervisor/docker/dns.py +++ b/supervisor/docker/dns.py @@ -18,7 +18,7 @@ class DockerDNS(DockerInterface, CoreSysAttributes): @property def image(self) -> str: """Return name of Supervisor DNS image.""" - return self.sys_dns.image + return self.sys_plugins.dns.image @property def name(self) -> str: @@ -40,7 +40,7 @@ class DockerDNS(DockerInterface, CoreSysAttributes): # Create & Run container docker_container = self.sys_docker.run( self.image, - version=self.sys_dns.version, + version=self.sys_plugins.dns.version, init=False, dns=False, ipv4=self.sys_docker.network.dns, diff --git a/supervisor/docker/homeassistant.py b/supervisor/docker/homeassistant.py index 95cc3e419..dd94b5963 100644 --- a/supervisor/docker/homeassistant.py +++ b/supervisor/docker/homeassistant.py @@ -72,11 +72,11 @@ class DockerHomeAssistant(DockerInterface): "bind": "/etc/pulse/client.conf", "mode": "ro", }, - str(self.sys_audio.path_extern_pulse): { + str(self.sys_plugins.audio.path_extern_pulse): { "bind": "/run/audio", "mode": "ro", }, - str(self.sys_audio.path_extern_asound): { + str(self.sys_plugins.audio.path_extern_asound): { "bind": "/etc/asound.conf", "mode": "ro", }, diff --git a/supervisor/docker/multicast.py b/supervisor/docker/multicast.py new file mode 100644 index 000000000..7a033d4ea --- /dev/null +++ b/supervisor/docker/multicast.py @@ -0,0 +1,59 @@ +"""HA Cli docker object.""" +from contextlib import suppress +import logging + +from ..const import DOCKER_NETWORK_MASK, ENV_HASSIO_NETWORK, ENV_TIME +from ..coresys import CoreSysAttributes +from ..exceptions import DockerAPIError +from .interface import DockerInterface + +_LOGGER: logging.Logger = logging.getLogger(__name__) + +MULTICAST_DOCKER_NAME: str = "hassio_multicast" + + +class DockerMulticast(DockerInterface, CoreSysAttributes): + """Docker Supervisor wrapper for HA multicast.""" + + @property + def image(self): + """Return name of HA multicast image.""" + return self.sys_plugins.multicast.image + + @property + def name(self) -> str: + """Return name of Docker container.""" + return MULTICAST_DOCKER_NAME + + def _run(self) -> None: + """Run Docker image. + + Need run inside executor. + """ + if self._is_running(): + return + + # Cleanup + with suppress(DockerAPIError): + self._stop() + + # Create & Run container + docker_container = self.sys_docker.run( + self.image, + version=self.sys_plugins.multicast.version, + init=False, + name=self.name, + hostname=self.name.replace("_", "-"), + network_mode="host", + detach=True, + extra_hosts={"supervisor": self.sys_docker.network.supervisor}, + environment={ + ENV_TIME: self.sys_timezone, + ENV_HASSIO_NETWORK: str(DOCKER_NETWORK_MASK), + }, + ) + + self._meta = docker_container.attrs + _LOGGER.info( + "Start Multicast %s with version %s - Host", self.image, self.version + ) diff --git a/supervisor/exceptions.py b/supervisor/exceptions.py index c5484daff..b6b89f004 100644 --- a/supervisor/exceptions.py +++ b/supervisor/exceptions.py @@ -61,10 +61,21 @@ class CliError(HassioError): """HA cli exception.""" -class CliUpdateError(HassOSError): +class CliUpdateError(CliError): """Error on update of a HA cli.""" +# Multicast + + +class MulticastError(HassioError): + """Multicast exception.""" + + +class MulticastUpdateError(MulticastError): + """Error on update of a multicast.""" + + # DNS diff --git a/supervisor/homeassistant.py b/supervisor/homeassistant.py index c9fc68f77..decc49bf3 100644 --- a/supervisor/homeassistant.py +++ b/supervisor/homeassistant.py @@ -636,7 +636,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes): def write_pulse(self): """Write asound config to file and return True on success.""" - pulse_config = self.sys_audio.pulse_client( + pulse_config = self.sys_plugins.audio.pulse_client( input_profile=self.audio_input, output_profile=self.audio_output ) diff --git a/supervisor/plugins/__init__.py b/supervisor/plugins/__init__.py index d2cffc4d9..c41c7cebf 100644 --- a/supervisor/plugins/__init__.py +++ b/supervisor/plugins/__init__.py @@ -1 +1,76 @@ """Plugin for Supervisor backend.""" +import asyncio +import logging + +from ..coresys import CoreSys, CoreSysAttributes +from .audio import Audio +from .cli import HaCli +from .dns import CoreDNS +from .multicast import Multicast + +_LOGGER: logging.Logger = logging.getLogger(__name__) + + +class PluginManager(CoreSysAttributes): + """Manage supported function for plugins.""" + + def __init__(self, coresys: CoreSys): + """Initialize plugin manager.""" + self.coresys: CoreSys = coresys + + self._cli: HaCli = HaCli(coresys) + self._dns: CoreDNS = CoreDNS(coresys) + self._audio: Audio = Audio(coresys) + self._multicast: Multicast = Multicast(coresys) + + @property + def cli(self) -> HaCli: + """Return cli handler.""" + return self._cli + + @property + def dns(self) -> CoreDNS: + """Return dns handler.""" + return self._dns + + @property + def audio(self) -> Audio: + """Return audio handler.""" + return self._audio + + @property + def multicast(self) -> Multicast: + """Return multicast handler.""" + return self._multicast + + async def load(self): + """Load Supervisor plugins.""" + await asyncio.wait( + [self.dns.load(), self.audio.load(), self.cli.load(), self.multicast.load()] + ) + + async def repair(self): + """Repair Supervisor plugins.""" + await asyncio.wait( + [ + self.dns.repair(), + self.audio.repair(), + self.cli.repair(), + self.multicast.repair(), + ] + ) + + async def unload(self) -> None: + """Unload Supervisor plugin.""" + await asyncio.wait([self.dns.unload()]) + + async def shutdown(self) -> None: + """Shutdown Supervisor plugin.""" + await asyncio.wait( + [ + self.dns.stop(), + self.audio.stop(), + self.cli.stop(), + self.multicast.stop(), + ] + ) diff --git a/supervisor/plugins/audio.py b/supervisor/plugins/audio.py index 1cd68a099..05a42ad7b 100644 --- a/supervisor/plugins/audio.py +++ b/supervisor/plugins/audio.py @@ -1,4 +1,7 @@ -"""Home Assistant control object.""" +"""Home Assistant audio plugin. + +Code: https://github.com/home-assistant/plugin-audio +""" import asyncio from contextlib import suppress import logging @@ -14,12 +17,12 @@ from ..docker.audio import DockerAudio from ..docker.stats import DockerStats from ..exceptions import AudioError, AudioUpdateError, DockerAPIError from ..utils.json import JsonConfig -from ..validate import SCHEMA_AUDIO_CONFIG +from .validate import SCHEMA_AUDIO_CONFIG _LOGGER: logging.Logger = logging.getLogger(__name__) -PULSE_CLIENT_TMPL: Path = Path(__file__).parents[0].joinpath("data/pulse-client.tmpl") -ASOUND_TMPL: Path = Path(__file__).parents[0].joinpath("data/asound.tmpl") +PULSE_CLIENT_TMPL: Path = Path(__file__).parents[1].joinpath("data/pulse-client.tmpl") +ASOUND_TMPL: Path = Path(__file__).parents[1].joinpath("data/asound.tmpl") class Audio(JsonConfig, CoreSysAttributes): @@ -177,7 +180,6 @@ class Audio(JsonConfig, CoreSysAttributes): async def start(self) -> None: """Run CoreDNS.""" - # Start Instance _LOGGER.info("Start Audio plugin") try: await self.instance.run() @@ -185,6 +187,15 @@ class Audio(JsonConfig, CoreSysAttributes): _LOGGER.error("Can't start Audio plugin") raise AudioError() from None + async def stop(self) -> None: + """Stop CoreDNS.""" + _LOGGER.info("Stop Audio plugin") + try: + await self.instance.stop() + except DockerAPIError: + _LOGGER.error("Can't stop Audio plugin") + raise AudioError() from None + def logs(self) -> Awaitable[bytes]: """Get CoreDNS docker logs. diff --git a/supervisor/plugins/cli.py b/supervisor/plugins/cli.py index 4d7e1fdc7..a26e0ee94 100644 --- a/supervisor/plugins/cli.py +++ b/supervisor/plugins/cli.py @@ -1,4 +1,7 @@ -"""CLI support on supervisor.""" +"""Home Assistant cli plugin. + +Code: https://github.com/home-assistant/plugin-cli +""" import asyncio from contextlib import suppress import logging @@ -11,7 +14,7 @@ from ..docker.cli import DockerCli from ..docker.stats import DockerStats from ..exceptions import CliError, CliUpdateError, DockerAPIError from ..utils.json import JsonConfig -from ..validate import SCHEMA_CLI_CONFIG +from .validate import SCHEMA_CLI_CONFIG _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -158,6 +161,15 @@ class HaCli(CoreSysAttributes, JsonConfig): _LOGGER.error("Can't start cli plugin") raise CliError() from None + async def stop(self) -> None: + """Stop cli.""" + _LOGGER.info("Stop cli plugin") + try: + await self.instance.stop() + except DockerAPIError: + _LOGGER.error("Can't stop cli plugin") + raise CliError() from None + async def stats(self) -> DockerStats: """Return stats of cli.""" try: diff --git a/supervisor/plugins/dns.py b/supervisor/plugins/dns.py index c6e9ec30b..f7c8abd63 100644 --- a/supervisor/plugins/dns.py +++ b/supervisor/plugins/dns.py @@ -1,4 +1,7 @@ -"""Home Assistant control object.""" +"""Home Assistant dns plugin. + +Code: https://github.com/home-assistant/plugin-dns +""" import asyncio from contextlib import suppress from ipaddress import IPv4Address @@ -17,12 +20,13 @@ from ..docker.stats import DockerStats from ..exceptions import CoreDNSError, CoreDNSUpdateError, DockerAPIError from ..misc.forwarder import DNSForward from ..utils.json import JsonConfig -from ..validate import SCHEMA_DNS_CONFIG, dns_url +from .validate import SCHEMA_DNS_CONFIG +from ..validate import dns_url _LOGGER: logging.Logger = logging.getLogger(__name__) -COREDNS_TMPL: Path = Path(__file__).parents[0].joinpath("data/coredns.tmpl") -RESOLV_TMPL: Path = Path(__file__).parents[0].joinpath("data/resolv.tmpl") +COREDNS_TMPL: Path = Path(__file__).parents[1].joinpath("data/coredns.tmpl") +RESOLV_TMPL: Path = Path(__file__).parents[1].joinpath("data/resolv.tmpl") HOST_RESOLV: Path = Path("/etc/resolv.conf") @@ -212,8 +216,12 @@ class CoreDNS(JsonConfig, CoreSysAttributes): async def restart(self) -> None: """Restart CoreDNS plugin.""" self._write_corefile() - with suppress(DockerAPIError): + _LOGGER.info("Restart CoreDNS plugin") + try: await self.instance.restart() + except DockerAPIError: + _LOGGER.error("Can't start CoreDNS plugin") + raise CoreDNSError() async def start(self) -> None: """Run CoreDNS.""" @@ -227,6 +235,15 @@ class CoreDNS(JsonConfig, CoreSysAttributes): _LOGGER.error("Can't start CoreDNS plugin") raise CoreDNSError() from None + async def stop(self) -> None: + """Stop CoreDNS.""" + _LOGGER.info("Stop CoreDNS plugin") + try: + await self.instance.stop() + except DockerAPIError: + _LOGGER.error("Can't stop CoreDNS plugin") + raise CoreDNSError() from None + async def reset(self) -> None: """Reset DNS and hosts.""" # Reset manually defined DNS diff --git a/supervisor/plugins/multicast.py b/supervisor/plugins/multicast.py new file mode 100644 index 000000000..088b97f3f --- /dev/null +++ b/supervisor/plugins/multicast.py @@ -0,0 +1,208 @@ +"""Home Assistant multicast plugin. + +Code: https://github.com/home-assistant/plugin-multicast +""" +import asyncio +from contextlib import suppress +import logging +from typing import Awaitable, Optional + +from ..const import ATTR_IMAGE, ATTR_VERSION, FILE_HASSIO_MULTICAST +from ..coresys import CoreSys, CoreSysAttributes +from ..docker.multicast import DockerMulticast +from ..docker.stats import DockerStats +from ..exceptions import DockerAPIError, MulticastError, MulticastUpdateError +from ..utils.json import JsonConfig +from .validate import SCHEMA_MULTICAST_CONFIG + +_LOGGER: logging.Logger = logging.getLogger(__name__) + + +class Multicast(JsonConfig, CoreSysAttributes): + """Home Assistant core object for handle it.""" + + def __init__(self, coresys: CoreSys): + """Initialize hass object.""" + super().__init__(FILE_HASSIO_MULTICAST, SCHEMA_MULTICAST_CONFIG) + self.coresys: CoreSys = coresys + self.instance: DockerMulticast = DockerMulticast(coresys) + + @property + def version(self) -> Optional[str]: + """Return current version of Multicast.""" + return self._data.get(ATTR_VERSION) + + @version.setter + def version(self, value: str) -> None: + """Return current version of Multicast.""" + self._data[ATTR_VERSION] = value + + @property + def image(self) -> str: + """Return current image of Multicast.""" + if self._data.get(ATTR_IMAGE): + return self._data[ATTR_IMAGE] + return f"homeassistant/{self.sys_arch.supervisor}-hassio-multicast" + + @image.setter + def image(self, value: str) -> None: + """Return current image of Multicast.""" + self._data[ATTR_IMAGE] = value + + @property + def latest_version(self) -> Optional[str]: + """Return latest version of Multicast.""" + return self.sys_updater.version_multicast + + @property + def in_progress(self) -> bool: + """Return True if a task is in progress.""" + return self.instance.in_progress + + @property + def need_update(self) -> bool: + """Return True if an update is available.""" + return self.version != self.latest_version + + async def load(self) -> None: + """Load multicast setup.""" + # Check Multicast state + try: + # Evaluate Version if we lost this information + if not self.version: + self.version = await self.instance.get_latest_version(key=int) + + await self.instance.attach(tag=self.version) + except DockerAPIError: + _LOGGER.info( + "No Multicast plugin Docker image %s found.", self.instance.image + ) + + # Install Multicast plugin + with suppress(MulticastError): + await self.install() + else: + self.version = self.instance.version + self.image = self.instance.image + self.save_data() + + # Run Multicast plugin + with suppress(MulticastError): + if await self.instance.is_running(): + await self.restart() + else: + await self.start() + + async def install(self) -> None: + """Install Multicast.""" + _LOGGER.info("Setup Multicast plugin") + while True: + # read homeassistant tag and install it + if not self.latest_version: + await self.sys_updater.reload() + + if self.latest_version: + with suppress(DockerAPIError): + await self.instance.install( + self.latest_version, image=self.sys_updater.image_multicast + ) + break + _LOGGER.warning("Error on install Multicast plugin. Retry in 30sec") + await asyncio.sleep(30) + + _LOGGER.info("Multicast plugin now installed") + self.version = self.instance.version + self.image = self.sys_updater.image_multicast + self.save_data() + + async def update(self, version: Optional[str] = None) -> None: + """Update Multicast plugin.""" + version = version or self.latest_version + old_image = self.image + + if version == self.version: + _LOGGER.warning("Version %s is already installed for Multicast", version) + return + + # Update + try: + await self.instance.update(version, image=self.sys_updater.image_multicast) + except DockerAPIError: + _LOGGER.error("Multicast update fails") + raise MulticastUpdateError() from None + else: + self.version = version + self.image = self.sys_updater.image_multicast + self.save_data() + + # Cleanup + with suppress(DockerAPIError): + await self.instance.cleanup(old_image=old_image) + + # Start Multicast plugin + await self.start() + + async def restart(self) -> None: + """Restart Multicast plugin.""" + _LOGGER.info("Restart Multicast plugin") + try: + await self.instance.restart() + except DockerAPIError: + _LOGGER.error("Can't start Multicast plugin") + raise MulticastError() + + async def start(self) -> None: + """Run Multicast.""" + _LOGGER.info("Start Multicast plugin") + try: + await self.instance.run() + except DockerAPIError: + _LOGGER.error("Can't start Multicast plugin") + raise MulticastError() + + async def stop(self) -> None: + """Stop Multicast.""" + _LOGGER.info("Stop Multicast plugin") + try: + await self.instance.stop() + except DockerAPIError: + _LOGGER.error("Can't stop Multicast plugin") + raise MulticastError() + + def logs(self) -> Awaitable[bytes]: + """Get Multicast docker logs. + + Return Coroutine. + """ + return self.instance.logs() + + async def stats(self) -> DockerStats: + """Return stats of Multicast.""" + try: + return await self.instance.stats() + except DockerAPIError: + raise MulticastError() from None + + def is_running(self) -> Awaitable[bool]: + """Return True if Docker container is running. + + Return a coroutine. + """ + return self.instance.is_running() + + def is_fails(self) -> Awaitable[bool]: + """Return True if a Docker container is fails state. + Return a coroutine. + """ + return self.instance.is_fails() + + async def repair(self) -> None: + """Repair Multicast plugin.""" + if await self.instance.exists(): + return + + _LOGGER.info("Repair Multicast %s", self.version) + try: + await self.instance.install(self.version) + except DockerAPIError: + _LOGGER.error("Repairing of Multicast fails") diff --git a/supervisor/plugins/validate.py b/supervisor/plugins/validate.py new file mode 100644 index 000000000..346884afa --- /dev/null +++ b/supervisor/plugins/validate.py @@ -0,0 +1,44 @@ +"""Validate functions.""" + +import voluptuous as vol + +from ..const import ATTR_ACCESS_TOKEN, ATTR_IMAGE, ATTR_SERVERS, ATTR_VERSION +from ..validate import dns_server_list, docker_image, token + + +SCHEMA_DNS_CONFIG = vol.Schema( + { + vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, + vol.Optional(ATTR_SERVERS, default=list): dns_server_list, + }, + extra=vol.REMOVE_EXTRA, +) + + +SCHEMA_AUDIO_CONFIG = vol.Schema( + { + vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, + }, + extra=vol.REMOVE_EXTRA, +) + + +SCHEMA_CLI_CONFIG = vol.Schema( + { + vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, + vol.Optional(ATTR_ACCESS_TOKEN): token, + }, + extra=vol.REMOVE_EXTRA, +) + + +SCHEMA_MULTICAST_CONFIG = vol.Schema( + { + vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), + vol.Optional(ATTR_IMAGE): docker_image, + }, + extra=vol.REMOVE_EXTRA, +) diff --git a/supervisor/tasks.py b/supervisor/tasks.py index 4aa569e40..c146c8c39 100644 --- a/supervisor/tasks.py +++ b/supervisor/tasks.py @@ -3,7 +3,13 @@ import asyncio import logging from .coresys import CoreSysAttributes -from .exceptions import AudioError, CliError, CoreDNSError, HomeAssistantError +from .exceptions import ( + AudioError, + CliError, + CoreDNSError, + HomeAssistantError, + MulticastError, +) _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -14,6 +20,7 @@ RUN_UPDATE_ADDONS = 57600 RUN_UPDATE_CLI = 28100 RUN_UPDATE_DNS = 30100 RUN_UPDATE_AUDIO = 30200 +RUN_UPDATE_MULTICAST = 30300 RUN_RELOAD_ADDONS = 10800 RUN_RELOAD_SNAPSHOTS = 72000 @@ -27,6 +34,7 @@ RUN_WATCHDOG_HOMEASSISTANT_API = 300 RUN_WATCHDOG_DNS_DOCKER = 20 RUN_WATCHDOG_AUDIO_DOCKER = 30 RUN_WATCHDOG_CLI_DOCKER = 40 +RUN_WATCHDOG_MULTICAST_DOCKER = 50 class Tasks(CoreSysAttributes): @@ -58,6 +66,11 @@ class Tasks(CoreSysAttributes): self.jobs.add( self.sys_scheduler.register_task(self._update_audio, RUN_UPDATE_AUDIO) ) + self.jobs.add( + self.sys_scheduler.register_task( + self._update_multicast, RUN_UPDATE_MULTICAST + ) + ) # Reload self.jobs.add( @@ -108,6 +121,11 @@ class Tasks(CoreSysAttributes): self._watchdog_cli_docker, RUN_WATCHDOG_CLI_DOCKER ) ) + self.jobs.add( + self.sys_scheduler.register_task( + self._watchdog_multicast_docker, RUN_WATCHDOG_MULTICAST_DOCKER + ) + ) _LOGGER.info("All core tasks are scheduled") @@ -209,66 +227,92 @@ class Tasks(CoreSysAttributes): async def _update_cli(self): """Check and run update of cli.""" - if not self.sys_cli.need_update: + if not self.sys_plugins.cli.need_update: return _LOGGER.info("Found new cli version") - await self.sys_cli.update() + await self.sys_plugins.cli.update() async def _update_dns(self): """Check and run update of CoreDNS plugin.""" - if not self.sys_dns.need_update: + if not self.sys_plugins.dns.need_update: return _LOGGER.info("Found new CoreDNS plugin version") - await self.sys_dns.update() + await self.sys_plugins.dns.update() async def _update_audio(self): """Check and run update of PulseAudio plugin.""" - if not self.sys_audio.need_update: + if not self.sys_plugins.audio.need_update: return _LOGGER.info("Found new PulseAudio plugin version") - await self.sys_audio.update() + await self.sys_plugins.audio.update() + + async def _update_multicast(self): + """Check and run update of multicast.""" + if not self.sys_plugins.multicast.need_update: + return + + _LOGGER.info("Found new Multicast version") + await self.sys_plugins.multicast.update() async def _watchdog_dns_docker(self): """Check running state of Docker and start if they is close.""" # if CoreDNS is active - if await self.sys_dns.is_running() or self.sys_dns.in_progress: + if await self.sys_plugins.dns.is_running() or self.sys_plugins.dns.in_progress: return _LOGGER.warning("Watchdog found a problem with CoreDNS plugin!") # Reset of fails - if await self.sys_dns.is_fails(): + if await self.sys_plugins.dns.is_fails(): _LOGGER.error("CoreDNS plugin is in fails state / Reset config") - await self.sys_dns.reset() - await self.sys_dns.loop_detection() + await self.sys_plugins.dns.reset() + await self.sys_plugins.dns.loop_detection() try: - await self.sys_dns.start() + await self.sys_plugins.dns.start() except CoreDNSError: _LOGGER.error("Watchdog CoreDNS reanimation fails!") async def _watchdog_audio_docker(self): """Check running state of Docker and start if they is close.""" # if PulseAudio plugin is active - if await self.sys_audio.is_running() or self.sys_audio.in_progress: + if ( + await self.sys_plugins.audio.is_running() + or self.sys_plugins.audio.in_progress + ): return _LOGGER.warning("Watchdog found a problem with PulseAudio plugin!") try: - await self.sys_audio.start() + await self.sys_plugins.audio.start() except AudioError: _LOGGER.error("Watchdog PulseAudio reanimation fails!") async def _watchdog_cli_docker(self): """Check running state of Docker and start if they is close.""" # if cli plugin is active - if await self.sys_cli.is_running() or self.sys_cli.in_progress: + if await self.sys_plugins.cli.is_running() or self.sys_plugins.cli.in_progress: return _LOGGER.warning("Watchdog found a problem with cli plugin!") try: - await self.sys_cli.start() + await self.sys_plugins.cli.start() except CliError: _LOGGER.error("Watchdog cli reanimation fails!") + + async def _watchdog_multicast_docker(self): + """Check running state of Docker and start if they is close.""" + # if multicast plugin is active + if ( + await self.sys_plugins.multicast.is_running() + or self.sys_plugins.multicast.in_progress + ): + return + _LOGGER.warning("Watchdog found a problem with Multicast plugin!") + + try: + await self.sys_plugins.multicast.start() + except MulticastError: + _LOGGER.error("Watchdog Multicast reanimation fails!") diff --git a/supervisor/updater.py b/supervisor/updater.py index cffe86e84..81f8aa109 100644 --- a/supervisor/updater.py +++ b/supervisor/updater.py @@ -13,10 +13,11 @@ from .const import ( ATTR_CHANNEL, ATTR_CLI, ATTR_DNS, - ATTR_SUPERVISOR, ATTR_HASSOS, ATTR_HOMEASSISTANT, ATTR_IMAGE, + ATTR_MULTICAST, + ATTR_SUPERVISOR, FILE_HASSIO_UPDATER, URL_HASSIO_VERSION, UpdateChannels, @@ -78,6 +79,11 @@ class Updater(JsonConfig, CoreSysAttributes): """Return latest version of Audio.""" return self._data.get(ATTR_AUDIO) + @property + def version_multicast(self) -> Optional[str]: + """Return latest version of Multicast.""" + return self._data.get(ATTR_MULTICAST) + @property def image_homeassistant(self) -> Optional[str]: """Return latest version of Home Assistant.""" @@ -123,6 +129,15 @@ class Updater(JsonConfig, CoreSysAttributes): .format(arch=self.sys_arch.supervisor) ) + @property + def image_multicast(self) -> Optional[str]: + """Return latest version of Multicast.""" + return ( + self._data[ATTR_IMAGE] + .get(ATTR_MULTICAST, "") + .format(arch=self.sys_arch.supervisor) + ) + @property def channel(self) -> UpdateChannels: """Return upstream channel of Supervisor instance.""" @@ -171,10 +186,11 @@ class Updater(JsonConfig, CoreSysAttributes): if self.sys_hassos.board: self._data[ATTR_HASSOS] = data["hassos"][self.sys_hassos.board] - # Update Home Assistant services + # Update Home Assistant plugins self._data[ATTR_CLI] = data["cli"] self._data[ATTR_DNS] = data["dns"] self._data[ATTR_AUDIO] = data["audio"] + self._data[ATTR_MULTICAST] = data["multicast"] # Update images for that versions self._data[ATTR_IMAGE][ATTR_HOMEASSISTANT] = data["image"]["core"] @@ -182,6 +198,7 @@ class Updater(JsonConfig, CoreSysAttributes): self._data[ATTR_IMAGE][ATTR_AUDIO] = data["image"]["audio"] self._data[ATTR_IMAGE][ATTR_CLI] = data["image"]["cli"] self._data[ATTR_IMAGE][ATTR_DNS] = data["image"]["dns"] + self._data[ATTR_IMAGE][ATTR_MULTICAST] = data["image"]["multicast"] except KeyError as err: _LOGGER.warning("Can't process version data: %s", err) diff --git a/supervisor/validate.py b/supervisor/validate.py index ceb45a6ec..4e502073b 100644 --- a/supervisor/validate.py +++ b/supervisor/validate.py @@ -17,18 +17,18 @@ from .const import ( ATTR_DEBUG, ATTR_DEBUG_BLOCK, ATTR_DNS, - ATTR_SUPERVISOR, ATTR_HASSOS, ATTR_HOMEASSISTANT, ATTR_IMAGE, ATTR_LAST_BOOT, ATTR_LOGGING, + ATTR_MULTICAST, ATTR_PORT, ATTR_PORTS, ATTR_REFRESH_TOKEN, - ATTR_SERVERS, ATTR_SESSION, ATTR_SSL, + ATTR_SUPERVISOR, ATTR_TIMEZONE, ATTR_UUID, ATTR_VERSION, @@ -129,6 +129,7 @@ SCHEMA_UPDATER_CONFIG = vol.Schema( vol.Optional(ATTR_CLI): vol.Coerce(str), vol.Optional(ATTR_DNS): vol.Coerce(str), vol.Optional(ATTR_AUDIO): vol.Coerce(str), + vol.Optional(ATTR_MULTICAST): vol.Coerce(str), vol.Optional(ATTR_IMAGE, default=dict): vol.Schema( { vol.Optional(ATTR_HOMEASSISTANT): docker_image, @@ -136,6 +137,7 @@ SCHEMA_UPDATER_CONFIG = vol.Schema( vol.Optional(ATTR_CLI): docker_image, vol.Optional(ATTR_DNS): docker_image, vol.Optional(ATTR_AUDIO): docker_image, + vol.Optional(ATTR_MULTICAST): docker_image, }, extra=vol.REMOVE_EXTRA, ), @@ -176,32 +178,3 @@ SCHEMA_INGRESS_CONFIG = vol.Schema( }, extra=vol.REMOVE_EXTRA, ) - - -SCHEMA_DNS_CONFIG = vol.Schema( - { - vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), - vol.Optional(ATTR_IMAGE): docker_image, - vol.Optional(ATTR_SERVERS, default=list): dns_server_list, - }, - extra=vol.REMOVE_EXTRA, -) - - -SCHEMA_AUDIO_CONFIG = vol.Schema( - { - vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), - vol.Optional(ATTR_IMAGE): docker_image, - }, - extra=vol.REMOVE_EXTRA, -) - - -SCHEMA_CLI_CONFIG = vol.Schema( - { - vol.Optional(ATTR_VERSION): vol.Maybe(vol.Coerce(str)), - vol.Optional(ATTR_IMAGE): docker_image, - vol.Optional(ATTR_ACCESS_TOKEN): token, - }, - extra=vol.REMOVE_EXTRA, -) From e787e59b495b90114db0f5b0252a0392751d24c3 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Mon, 6 Apr 2020 14:00:52 +0200 Subject: [PATCH 11/17] Cleanup network (#1637) * Cleanup network forward * Add permission --- supervisor/api/security.py | 1 + supervisor/const.py | 1 - supervisor/docker/multicast.py | 7 ++----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/supervisor/api/security.py b/supervisor/api/security.py index 559104c43..78a2a47ad 100644 --- a/supervisor/api/security.py +++ b/supervisor/api/security.py @@ -76,6 +76,7 @@ ADDONS_ROLE_ACCESS = { r"|/audio/.*" r"|/dns/.*" r"|/cli/.*" + r"|/multicast/.*" r"|/core/.+" r"|/homeassistant/.+" r"|/host/.+" diff --git a/supervisor/const.py b/supervisor/const.py index 7bb025e8e..513ba6c1f 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -68,7 +68,6 @@ HEADER_TOKEN_OLD = "X-Hassio-Key" ENV_TOKEN_OLD = "HASSIO_TOKEN" ENV_TOKEN = "SUPERVISOR_TOKEN" ENV_TIME = "TZ" -ENV_HASSIO_NETWORK = "HASSIO_NETWORK" ENV_HOMEASSISTANT_REPOSITORY = "HOMEASSISTANT_REPOSITORY" ENV_SUPERVISOR_SHARE = "SUPERVISOR_SHARE" diff --git a/supervisor/docker/multicast.py b/supervisor/docker/multicast.py index 7a033d4ea..6168d89cc 100644 --- a/supervisor/docker/multicast.py +++ b/supervisor/docker/multicast.py @@ -2,7 +2,7 @@ from contextlib import suppress import logging -from ..const import DOCKER_NETWORK_MASK, ENV_HASSIO_NETWORK, ENV_TIME +from ..const import ENV_TIME from ..coresys import CoreSysAttributes from ..exceptions import DockerAPIError from .interface import DockerInterface @@ -47,10 +47,7 @@ class DockerMulticast(DockerInterface, CoreSysAttributes): network_mode="host", detach=True, extra_hosts={"supervisor": self.sys_docker.network.supervisor}, - environment={ - ENV_TIME: self.sys_timezone, - ENV_HASSIO_NETWORK: str(DOCKER_NETWORK_MASK), - }, + environment={ENV_TIME: self.sys_timezone}, ) self._meta = docker_container.attrs From e2dc1a447111f11e7b62a72580d908c99a8e69b1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Tue, 7 Apr 2020 12:08:29 +0200 Subject: [PATCH 12/17] Support plugin requirements & mdns (#1638) * Support plugin requirements & mdns * better error handling * Use debug from LogLevel * fix lint * fix logger * fix test env * Use new style * fix typo --- scripts/test_env.sh | 2 +- supervisor/api/supervisor.py | 5 +++-- supervisor/bootstrap.py | 15 +++++++------- supervisor/config.py | 7 ++++--- supervisor/const.py | 10 +++++++++ supervisor/data/coredns.tmpl | 13 +++++++----- supervisor/plugins/__init__.py | 37 ++++++++++++++++++++++++++++++++++ supervisor/plugins/dns.py | 15 +++++++++++--- supervisor/validate.py | 4 ++-- 9 files changed, 85 insertions(+), 23 deletions(-) diff --git a/scripts/test_env.sh b/scripts/test_env.sh index fface59b2..fb2b93567 100755 --- a/scripts/test_env.sh +++ b/scripts/test_env.sh @@ -96,7 +96,7 @@ function setup_test_env() { -e SUPERVISOR_SHARE="/workspaces/test_supervisor" \ -e SUPERVISOR_NAME=hassio_supervisor \ -e SUPERVISOR_DEV=1 \ - -e HOMEASSISTANT_REPOSITORY="homeassistant/qemux86-64-homeassistant" \ + -e SUPERVISOR_MACHINE="qemux86-64" \ homeassistant/amd64-hassio-supervisor:latest } diff --git a/supervisor/api/supervisor.py b/supervisor/api/supervisor.py index 33b71613b..241466036 100644 --- a/supervisor/api/supervisor.py +++ b/supervisor/api/supervisor.py @@ -38,11 +38,12 @@ from ..const import ( CONTENT_TYPE_BINARY, SUPERVISOR_VERSION, UpdateChannels, + LogLevel, ) from ..coresys import CoreSysAttributes from ..exceptions import APIError from ..utils.validate import validate_timezone -from ..validate import log_level, repositories, wait_boot +from ..validate import repositories, wait_boot from .utils import api_process, api_process_raw, api_validate _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -54,7 +55,7 @@ SCHEMA_OPTIONS = vol.Schema( vol.Optional(ATTR_ADDONS_REPOSITORIES): repositories, vol.Optional(ATTR_TIMEZONE): validate_timezone, vol.Optional(ATTR_WAIT_BOOT): wait_boot, - vol.Optional(ATTR_LOGGING): log_level, + vol.Optional(ATTR_LOGGING): vol.Coerce(LogLevel), vol.Optional(ATTR_DEBUG): vol.Boolean(), vol.Optional(ATTR_DEBUG_BLOCK): vol.Boolean(), } diff --git a/supervisor/bootstrap.py b/supervisor/bootstrap.py index 932180ca4..74bbcfc1d 100644 --- a/supervisor/bootstrap.py +++ b/supervisor/bootstrap.py @@ -12,12 +12,13 @@ from .api import RestAPI from .arch import CpuArch from .auth import Auth from .const import ( - SOCKET_DOCKER, - UpdateChannels, - ENV_SUPERVISOR_SHARE, - ENV_SUPERVISOR_NAME, ENV_HOMEASSISTANT_REPOSITORY, ENV_SUPERVISOR_MACHINE, + ENV_SUPERVISOR_NAME, + ENV_SUPERVISOR_SHARE, + SOCKET_DOCKER, + LogLevel, + UpdateChannels, ) from .core import Core from .coresys import CoreSys @@ -28,14 +29,14 @@ from .homeassistant import HomeAssistant from .host import HostManager from .hwmon import HwMonitor from .ingress import Ingress +from .plugins import PluginManager +from .secrets import SecretsManager from .services import ServiceManager from .snapshots import SnapshotManager from .store import StoreManager from .supervisor import Supervisor from .tasks import Tasks from .updater import Updater -from .secrets import SecretsManager -from .plugins import PluginManager from .utils.dt import fetch_timezone _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -163,7 +164,7 @@ def initialize_system_data(coresys: CoreSys): if bool(os.environ.get("SUPERVISOR_DEV", 0)): _LOGGER.warning("SUPERVISOR_DEV is set") coresys.updater.channel = UpdateChannels.DEV - coresys.config.logging = "debug" + coresys.config.logging = LogLevel.DEBUG coresys.config.debug = True diff --git a/supervisor/config.py b/supervisor/config.py index e4fb771d5..3354bf467 100644 --- a/supervisor/config.py +++ b/supervisor/config.py @@ -15,6 +15,7 @@ from .const import ( ENV_SUPERVISOR_SHARE, FILE_HASSIO_CONFIG, SUPERVISOR_DATA, + LogLevel, ) from .utils.dt import parse_datetime from .utils.json import JsonConfig @@ -89,19 +90,19 @@ class CoreConfig(JsonConfig): self._data[ATTR_DEBUG_BLOCK] = value @property - def logging(self) -> str: + def logging(self) -> LogLevel: """Return log level of system.""" return self._data[ATTR_LOGGING] @logging.setter - def logging(self, value: str): + def logging(self, value: LogLevel): """Set system log level.""" self._data[ATTR_LOGGING] = value self.modify_log_level() def modify_log_level(self) -> None: """Change log level.""" - lvl = getattr(logging, self.logging.upper()) + lvl = getattr(logging, str(self.logging.value).upper()) logging.getLogger("supervisor").setLevel(lvl) @property diff --git a/supervisor/const.py b/supervisor/const.py index 513ba6c1f..5b92de955 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -361,3 +361,13 @@ class CoreStates(str, Enum): STARTUP = "startup" RUNNING = "running" FREEZE = "freeze" + + +class LogLevel(str, Enum): + """Logging level of system.""" + + DEBUG = "debug" + INFO = "info" + WARNING = "warning" + ERROR = "error" + CRITICAL = "critical" diff --git a/supervisor/data/coredns.tmpl b/supervisor/data/coredns.tmpl index 50b6dc540..33e3bbdaa 100644 --- a/supervisor/data/coredns.tmpl +++ b/supervisor/data/coredns.tmpl @@ -2,26 +2,29 @@ log errors loop + {% if debug %}debug{% endif %} hosts /config/hosts { fallthrough } template ANY AAAA local.hass.io hassio { rcode NOERROR } - forward . {{ locals | join(" ") }} dns://127.0.0.1:5353 { + mdns + forward . {{ locals | join(" ") }} dns://127.0.0.1:5553 { except local.hass.io policy sequential health_check 5s } - fallback REFUSED . dns://127.0.0.1:5353 - fallback SERVFAIL . dns://127.0.0.1:5353 - fallback NXDOMAIN . dns://127.0.0.1:5353 + fallback REFUSED . dns://127.0.0.1:5553 + fallback SERVFAIL . dns://127.0.0.1:5553 + fallback NXDOMAIN . dns://127.0.0.1:5553 cache 10 } -.:5353 { +.:5553 { log errors + {% if debug %}debug{% endif %} forward . tls://1.1.1.1 tls://1.0.0.1 { tls_servername cloudflare-dns.com except local.hass.io diff --git a/supervisor/plugins/__init__.py b/supervisor/plugins/__init__.py index c41c7cebf..0c0d91614 100644 --- a/supervisor/plugins/__init__.py +++ b/supervisor/plugins/__init__.py @@ -3,6 +3,7 @@ import asyncio import logging from ..coresys import CoreSys, CoreSysAttributes +from ..exceptions import HassioError from .audio import Audio from .cli import HaCli from .dns import CoreDNS @@ -14,6 +15,11 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) class PluginManager(CoreSysAttributes): """Manage supported function for plugins.""" + required_cli: int = 24 + required_dns: int = 5 + required_audio: int = 14 + required_multicast: int = 2 + def __init__(self, coresys: CoreSys): """Initialize plugin manager.""" self.coresys: CoreSys = coresys @@ -49,6 +55,37 @@ class PluginManager(CoreSysAttributes): [self.dns.load(), self.audio.load(), self.cli.load(), self.multicast.load()] ) + # Check requirements + for plugin, required_version in ( + (self._audio, self.required_audio), + (self._dns, self.required_dns), + (self._cli, self.required_cli), + (self._multicast, self.required_multicast), + ): + # Check if need an update + try: + if int(plugin.version) >= required_version: + continue + except TypeError: + _LOGGER.warning( + "Somethings going wrong with requirements on %s", + type(plugin).__name__, + ) + + _LOGGER.info( + "Requirement need update for %s - %i", + type(plugin).__name__, + required_version, + ) + try: + await plugin.update(version=str(required_version)) + except HassioError: + _LOGGER.error( + "Can't update %s to %i but it's a reuirement, the Supervisor is not health now!", + type(plugin).__name__, + required_version, + ) + async def repair(self): """Repair Supervisor plugins.""" await asyncio.wait( diff --git a/supervisor/plugins/dns.py b/supervisor/plugins/dns.py index f7c8abd63..0922cb182 100644 --- a/supervisor/plugins/dns.py +++ b/supervisor/plugins/dns.py @@ -13,15 +13,22 @@ import attr import jinja2 import voluptuous as vol -from ..const import ATTR_IMAGE, ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, FILE_HASSIO_DNS +from ..const import ( + ATTR_IMAGE, + ATTR_SERVERS, + ATTR_VERSION, + DNS_SUFFIX, + FILE_HASSIO_DNS, + LogLevel, +) from ..coresys import CoreSys, CoreSysAttributes from ..docker.dns import DockerDNS from ..docker.stats import DockerStats from ..exceptions import CoreDNSError, CoreDNSUpdateError, DockerAPIError from ..misc.forwarder import DNSForward from ..utils.json import JsonConfig -from .validate import SCHEMA_DNS_CONFIG from ..validate import dns_url +from .validate import SCHEMA_DNS_CONFIG _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -301,7 +308,9 @@ class CoreDNS(JsonConfig, CoreSysAttributes): _LOGGER.warning("Ignore invalid DNS Server: %s", server) # Generate config file - data = self.coredns_template.render(locals=dns_servers) + data = self.coredns_template.render( + locals=dns_servers, debug=self.sys_config.logging == LogLevel.DEBUG + ) try: self.corefile.write_text(data) diff --git a/supervisor/validate.py b/supervisor/validate.py index 4e502073b..cfe3c9e22 100644 --- a/supervisor/validate.py +++ b/supervisor/validate.py @@ -34,6 +34,7 @@ from .const import ( ATTR_VERSION, ATTR_WAIT_BOOT, ATTR_WATCHDOG, + LogLevel, UpdateChannels, ) from .utils.validate import validate_timezone @@ -48,7 +49,6 @@ docker_image = vol.Match(r"^[\w{}]+/[\-\w{}]+$") uuid_match = vol.Match(r"^[0-9a-f]{32}$") sha256 = vol.Match(r"^[0-9a-f]{64}$") token = vol.Match(r"^[0-9a-f]{32,256}$") -log_level = vol.In(["debug", "info", "warning", "error", "critical"]) def dns_url(url: str) -> str: @@ -156,7 +156,7 @@ SCHEMA_SUPERVISOR_CONFIG = vol.Schema( default=["https://github.com/hassio-addons/repository"], ): repositories, vol.Optional(ATTR_WAIT_BOOT, default=5): wait_boot, - vol.Optional(ATTR_LOGGING, default="info"): log_level, + vol.Optional(ATTR_LOGGING, default=LogLevel.INFO): vol.Coerce(LogLevel), vol.Optional(ATTR_DEBUG, default=False): vol.Boolean(), vol.Optional(ATTR_DEBUG_BLOCK, default=False): vol.Boolean(), }, From c3f5ee43b68052991e435e0e2699c9e8b35f7572 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 8 Apr 2020 12:11:59 +0200 Subject: [PATCH 13/17] Add missing multicast/logs endpoint to API docs (#1640) --- API.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/API.md b/API.md index 252ab59fc..585abf3fd 100644 --- a/API.md +++ b/API.md @@ -956,6 +956,8 @@ return: - POST `/multicast/restart` +- GET `/multicast/logs` + - GET `/multicast/stats` ```json From ce84e185ad013275a44476a8fee14f863efed909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 8 Apr 2020 13:44:03 +0200 Subject: [PATCH 14/17] Restore repositories with partial snapshot (#1641) * Restore repositories with partial snapshot * Run black --- supervisor/snapshots/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/supervisor/snapshots/__init__.py b/supervisor/snapshots/__init__.py index 42b14cf56..64c5c8c45 100644 --- a/supervisor/snapshots/__init__.py +++ b/supervisor/snapshots/__init__.py @@ -310,6 +310,9 @@ class SnapshotManager(CoreSysAttributes): ) if addons: + _LOGGER.info("Restore %s run Repositories", snapshot.slug) + await snapshot.restore_repositories() + _LOGGER.info("Restore %s old add-ons", snapshot.slug) await snapshot.restore_addons(addons) From b2d74647909abbfdbe8e841281e83b75fc77fbc4 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 8 Apr 2020 14:08:09 +0200 Subject: [PATCH 15/17] Fix multicast API for logs (#1642) --- supervisor/api/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/supervisor/api/__init__.py b/supervisor/api/__init__.py index 2d0643a9c..b4bfe0c45 100644 --- a/supervisor/api/__init__.py +++ b/supervisor/api/__init__.py @@ -124,6 +124,7 @@ class RestAPI(CoreSysAttributes): [ web.get("/multicast/info", api_multicast.info), web.get("/multicast/stats", api_multicast.stats), + web.get("/multicast/logs", api_multicast.logs), web.post("/multicast/update", api_multicast.update), web.post("/multicast/restart", api_multicast.restart), ] From 13148ec7fbbacfcb913c21f1418d2fad42e062a3 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 8 Apr 2020 14:29:43 +0200 Subject: [PATCH 16/17] Filter add-ons in the store based on advanced mode (#1643) * Filter add-ons in the store based on advanced mode Signed-off-by: Pascal Vizeli * Filter add-ons in the store based on advanced mode p2 Signed-off-by: Pascal Vizeli --- home-assistant-polymer | 2 +- .../api/panel/chunk.05403bc1774dc39117f6.js | 2 ++ .../panel/chunk.05403bc1774dc39117f6.js.gz | Bin 0 -> 32131 bytes .../panel/chunk.05403bc1774dc39117f6.js.map | 1 + .../api/panel/chunk.d9ebbcff54094bd6592a.js | 3 +++ .../chunk.d9ebbcff54094bd6592a.js.LICENSE | 10 ++++++++++ .../panel/chunk.d9ebbcff54094bd6592a.js.gz | Bin 0 -> 6799 bytes .../panel/chunk.d9ebbcff54094bd6592a.js.map | 1 + .../api/panel/chunk.ec48cf4e7f87d6417353.js | 3 +++ .../chunk.ec48cf4e7f87d6417353.js.LICENSE | 10 ++++++++++ .../panel/chunk.ec48cf4e7f87d6417353.js.gz | Bin 0 -> 7355 bytes .../panel/chunk.ec48cf4e7f87d6417353.js.map | 1 + supervisor/api/panel/entrypoint.js | 2 +- supervisor/api/panel/entrypoint.js.gz | Bin 1530 -> 1528 bytes supervisor/api/panel/entrypoint.js.map | 2 +- supervisor/api/panel/manifest.json | 18 +++++++++--------- 16 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 supervisor/api/panel/chunk.05403bc1774dc39117f6.js create mode 100644 supervisor/api/panel/chunk.05403bc1774dc39117f6.js.gz create mode 100644 supervisor/api/panel/chunk.05403bc1774dc39117f6.js.map create mode 100644 supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js create mode 100644 supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js.LICENSE create mode 100644 supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js.gz create mode 100644 supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js.map create mode 100644 supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js create mode 100644 supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js.LICENSE create mode 100644 supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js.gz create mode 100644 supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js.map diff --git a/home-assistant-polymer b/home-assistant-polymer index ddb525f6c..ebb20abee 160000 --- a/home-assistant-polymer +++ b/home-assistant-polymer @@ -1 +1 @@ -Subproject commit ddb525f6cdee0206b7d82c3133f888b1a383f90c +Subproject commit ebb20abee0b3b227974684116ea2207d9ad33856 diff --git a/supervisor/api/panel/chunk.05403bc1774dc39117f6.js b/supervisor/api/panel/chunk.05403bc1774dc39117f6.js new file mode 100644 index 000000000..982c3a5e5 --- /dev/null +++ b/supervisor/api/panel/chunk.05403bc1774dc39117f6.js @@ -0,0 +1,2 @@ +(self.webpackJsonp=self.webpackJsonp||[]).push([[10],{10:function(e,t,r){"use strict";r.d(t,"a",function(){return s}),r.d(t,"b",function(){return c}),r.d(t,"c",function(){return l});var n=r(5);function i(){var e=a(["\n /* prevent clipping of positioned elements */\n paper-dialog-scrollable {\n --paper-dialog-scrollable: {\n -webkit-overflow-scrolling: auto;\n }\n }\n\n /* force smooth scrolling for iOS 10 */\n paper-dialog-scrollable.can-scroll {\n --paper-dialog-scrollable: {\n -webkit-overflow-scrolling: touch;\n }\n }\n\n .paper-dialog-buttons {\n align-items: flex-end;\n padding: 8px;\n }\n\n @media all and (min-width: 450px) {\n ha-paper-dialog {\n min-width: 400px;\n }\n }\n\n @media all and (max-width: 450px), all and (max-height: 500px) {\n paper-dialog,\n ha-paper-dialog {\n margin: 0;\n width: 100% !important;\n max-height: calc(100% - 64px);\n\n position: fixed !important;\n bottom: 0px;\n left: 0px;\n right: 0px;\n overflow: scroll;\n border-bottom-left-radius: 0px;\n border-bottom-right-radius: 0px;\n }\n }\n\n /* mwc-dialog (ha-dialog) styles */\n ha-dialog {\n --mdc-dialog-min-width: 400px;\n --mdc-dialog-max-width: 600px;\n --mdc-dialog-heading-ink-color: var(--primary-text-color);\n --mdc-dialog-content-ink-color: var(--primary-text-color);\n --justify-action-buttons: space-between;\n }\n\n ha-dialog .form {\n padding-bottom: 24px;\n color: var(--primary-text-color);\n }\n\n /* make dialog fullscreen on small screens */\n @media all and (max-width: 450px), all and (max-height: 500px) {\n ha-dialog {\n --mdc-dialog-min-width: 100vw;\n --mdc-dialog-max-height: 100vh;\n --mdc-dialog-shape-radius: 0px;\n --vertial-align-dialog: flex-end;\n }\n }\n mwc-button.warning {\n --mdc-theme-primary: var(--google-red-500);\n }\n .error {\n color: var(--google-red-500);\n }\n"]);return i=function(){return e},e}function o(){var e=a(["\n :host {\n @apply --paper-font-body1;\n }\n\n app-header-layout,\n ha-app-layout {\n background-color: var(--primary-background-color);\n }\n\n app-header,\n app-toolbar {\n background-color: var(--app-header-background-color);\n font-weight: 400;\n color: var(--app-header-text-color, white);\n }\n\n app-toolbar ha-menu-button + [main-title],\n app-toolbar ha-paper-icon-button-arrow-prev + [main-title],\n app-toolbar paper-icon-button + [main-title] {\n margin-left: 24px;\n }\n\n h1 {\n @apply --paper-font-title;\n }\n\n .secondary {\n color: var(--secondary-text-color);\n }\n\n .error {\n color: var(--google-red-500);\n }\n\n .warning {\n color: var(--google-red-500);\n }\n\n mwc-button.warning {\n --mdc-theme-primary: var(--google-red-500);\n }\n\n button.link {\n background: none;\n color: inherit;\n border: none;\n padding: 0;\n font: inherit;\n text-align: left;\n text-decoration: underline;\n cursor: pointer;\n }\n\n .card-actions a {\n text-decoration: none;\n }\n\n .card-actions .warning {\n --mdc-theme-primary: var(--google-red-500);\n }\n\n .layout.horizontal,\n .layout.vertical {\n display: flex;\n }\n .layout.inline {\n display: inline-flex;\n }\n .layout.horizontal {\n flex-direction: row;\n }\n .layout.vertical {\n flex-direction: column;\n }\n .layout.wrap {\n flex-wrap: wrap;\n }\n .layout.no-wrap {\n flex-wrap: nowrap;\n }\n .layout.center,\n .layout.center-center {\n align-items: center;\n }\n .layout.bottom {\n align-items: flex-end;\n }\n .layout.center-justified,\n .layout.center-center {\n justify-content: center;\n }\n .flex {\n flex: 1;\n flex-basis: 0.000000001px;\n }\n .flex-auto {\n flex: 1 1 auto;\n }\n .flex-none {\n flex: none;\n }\n .layout.justified {\n justify-content: space-between;\n }\n"]);return o=function(){return e},e}function a(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}var s={"paper-spinner-color":"var(--primary-color)","error-state-color":"var(--error-color)","state-icon-unavailable-color":"var(--disabled-text-color)","sidebar-text-color":"var(--primary-text-color)","sidebar-background-color":"var(--paper-listbox-background-color);","sidebar-selected-text-color":"var(--primary-color)","sidebar-selected-icon-color":"var(--primary-color)","sidebar-icon-color":"rgba(var(--rgb-primary-text-color), 0.6)","switch-checked-color":"var(--primary-color)","switch-checked-button-color":"var(--switch-checked-color, var(--primary-background-color))","switch-checked-track-color":"var(--switch-checked-color, #000000)","switch-unchecked-button-color":"var(--switch-unchecked-color, var(--primary-background-color))","switch-unchecked-track-color":"var(--switch-unchecked-color, #000000)","slider-color":"var(--primary-color)","slider-secondary-color":"var(--light-primary-color)","slider-bar-color":"var(--disabled-text-color)","label-badge-grey":"var(--paper-grey-500)","label-badge-background-color":"var(--card-background-color)","label-badge-text-color":"rgba(var(--rgb-primary-text-color), 0.8)","paper-card-background-color":"var(--card-background-color)","paper-listbox-background-color":"var(--card-background-color)","paper-item-icon-color":"var(--state-icon-color)","paper-item-icon-active-color":"var(--state-icon-active-color)","table-row-background-color":"var(--primary-background-color)","table-row-alternative-background-color":"var(--secondary-background-color)","paper-slider-knob-color":"var(--slider-color)","paper-slider-knob-start-color":"var(--slider-color)","paper-slider-pin-color":"var(--slider-color)","paper-slider-active-color":"var(--slider-color)","paper-slider-secondary-color":"var(--slider-secondary-color)","paper-slider-container-color":"var(--slider-bar-color)","data-table-background-color":"var(--card-background-color)","mdc-theme-primary":"var(--primary-color)","mdc-theme-secondary":"var(--accent-color)","mdc-theme-background":"var(--primary-background-color)","mdc-theme-surface":"var(--card-background-color)","mdc-theme-on-primary":"var(--text-primary-color)","mdc-theme-on-secondary":"var(--text-primary-color)","mdc-theme-on-surface":"var(--primary-text-color)","app-header-text-color":"var(--text-primary-color)","app-header-background-color":"var(--primary-color)","material-body-text-color":"var(--primary-text-color)","material-background-color":"var(--card-background-color)","material-secondary-background-color":"var(--secondary-background-color)"},c=Object(n.c)(o()),l=Object(n.c)(i())},102:function(e,t){},106:function(e,t,r){"use strict";var n=r(5),i=(r(92),r(107),r(47));function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(){var e=c(['\n :host {\n display: block;\n height: 100%;\n background-color: var(--primary-background-color);\n }\n\n .toolbar {\n display: flex;\n align-items: center;\n font-size: 20px;\n height: 65px;\n padding: 0 16px;\n pointer-events: none;\n background-color: var(--app-header-background-color);\n font-weight: 400;\n color: var(--app-header-text-color, white);\n border-bottom: var(--app-header-border-bottom, none);\n box-sizing: border-box;\n }\n\n ha-menu-button,\n ha-paper-icon-button-arrow-prev,\n ::slotted([slot="toolbar-icon"]) {\n pointer-events: auto;\n }\n\n ha-paper-icon-button-arrow-prev.hidden {\n visibility: hidden;\n }\n\n [main-title] {\n margin: 0 0 0 24px;\n line-height: 20px;\n flex-grow: 1;\n }\n\n .content {\n position: relative;\n width: 100%;\n height: calc(100% - 65px);\n overflow-y: auto;\n overflow: auto;\n -webkit-overflow-scrolling: touch;\n }\n ']);return a=function(){return e},e}function s(){var e=c(['\n
\n \n \n
\n
\n ']);return s=function(){return e},e}function c(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function l(e){return(l=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function d(e,t){return(d=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function f(e){var t,r=v(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function p(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function h(e){return e.decorators&&e.decorators.length}function m(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function y(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function v(e){var t=function(e,t){if("object"!==o(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==o(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===o(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!h(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n "]);return s=function(){return e},e}function c(){var e=u(["\n \n "]);return c=function(){return e},e}function l(){var e=u(["\n \n ",'\n \n
\n \n
\n ']);return l=function(){return e},e}function u(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function d(e){return(d=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function f(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function p(e,t){return(p=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function h(e){var t,r=g(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function m(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function y(e){return e.decorators&&e.decorators.length}function v(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function b(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function g(e){var t=function(e,t){if("object"!==o(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==o(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===o(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!y(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n .container {\n position: relative;\n display: inline-block;\n }\n\n mwc-button {\n transition: all 1s;\n }\n\n .success mwc-button {\n --mdc-theme-primary: white;\n background-color: var(--google-green-500);\n transition: none;\n }\n\n .error mwc-button {\n --mdc-theme-primary: white;\n background-color: var(--google-red-500);\n transition: none;\n }\n\n .progress {\n @apply --layout;\n @apply --layout-center-center;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n }\n \n
\n \n \n \n \n
\n ']);return a=function(){return e},e}function s(e,t){for(var r=0;r\n\n \n\n \n\n \n"),document.head.appendChild(a.content);var s={},c=function(e,t){if(t.themes[e]){for(var r,n,o=Object.assign({},i.a,{},t.themes[e]),a={},c={},l=0,u=Object.keys(o);l\n
\n

',"

\n \n go back\n \n
\n \n "]);return f=function(){return e},e}function p(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function h(e){return(h=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function m(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function y(e,t){return(y=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function v(e){var t,r=E(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function b(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function g(e){return e.decorators&&e.decorators.length}function w(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function k(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function E(e){var t=function(e,t){if("object"!==u(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==u(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===u(t)?t:String(t)}(function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!g(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n \n
\n You don\'t have any add-ons installed yet. Head over to\n \n

Add-ons

\n
\n ',"\n
\n
\n "]);return ce=function(){return e},e}function le(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function ue(e){return(ue=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function de(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function fe(e,t){return(fe=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function pe(e){var t,r=be(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function he(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function me(e){return e.decorators&&e.decorators.length}function ye(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function ve(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function be(e){var t=function(e,t){if("object"!==ie(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==ie(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===ie(t)?t:String(t)}(function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!me(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;at.name?1:-1}).map(function(t){return Object(n.e)(ae(),t,e._addonTapped,e.hass,t.name,t.description,t.installed!==t.version,t.installed!==t.version?"hassio:arrow-up-bold-circle":"hassio:puzzle","started"!==t.state?"Add-on is stopped":t.installed!==t.version?"New version available":"Add-on is running",t.installed&&t.installed!==t.version?"started"===t.state?"update":"update stopped":t.installed&&"started"===t.state?"running":"stopped",Object(ne.a)(e.hass.config.version,0,105)&&t.icon?"/api/hassio/addons/".concat(t.slug,"/icon"):void 0)}):Object(n.e)(se(),this._openStore))}},{kind:"get",static:!0,key:"styles",value:function(){return[i.b,re.a,Object(n.c)(oe())]}},{kind:"method",key:"_addonTapped",value:function(e){Object(O.a)(this,"/hassio/addon/".concat(e.currentTarget.addon.slug))}},{kind:"method",key:"_openStore",value:function(){Object(O.a)(this,"/hassio/store")}}]}},n.a),r(22),r(50);function ge(){var e=_e(["\n .icon {\n --iron-icon-height: 48px;\n --iron-icon-width: 48px;\n float: right;\n margin: 0 0 2px 10px;\n }\n .update-heading {\n font-size: var(--paper-font-subhead_-_font-size);\n font-weight: 500;\n margin-bottom: 0.5em;\n }\n .warning {\n color: var(--secondary-text-color);\n }\n .card-content {\n height: calc(100% - 47px);\n box-sizing: border-box;\n }\n .card-actions {\n text-align: right;\n }\n .errors {\n color: var(--google-red-500);\n padding: 16px;\n }\n a {\n text-decoration: none;\n }\n "]);return ge=function(){return e},e}function we(e){return(we="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function ke(){var e=_e(['\n
\n \n
\n ']);return ke=function(){return e},e}function Ee(){var e=_e(['\n \n
\n ','\n
'," ",'
\n
\n You are currently running version ','\n
\n
\n
\n "]);return Pe=function(){return e},e}function je(){var e=_e(['\n
\n ',"\n

\n ",'\n

\n
\n ',"\n ","\n ","\n
\n
\n "]);return je=function(){return e},e}function xe(){var e=_e([""]);return xe=function(){return e},e}function _e(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Se(e){return(Se=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function Ce(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function De(e,t){return(De=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Te(e){var t,r=Fe(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function Ae(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function ze(e){return e.decorators&&e.decorators.length}function Re(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function Ie(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function Fe(e){var t=function(e,t){if("object"!==we(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==we(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===we(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!ze(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a1?"Updates Available 🎉":"Update Available 🎉",this._renderUpdateCard("Home Assistant Core",this.hassInfo.version,this.hassInfo.version_latest,"hassio/homeassistant/update","https://".concat(this.hassInfo.version_latest.includes("b")?"rc":"www",".home-assistant.io/latest-release-notes/"),"hassio:home-assistant"),this._renderUpdateCard("Supervisor",this.supervisorInfo.version,this.supervisorInfo.version_latest,"hassio/supervisor/update","https://github.com//home-assistant/hassio/releases/tag/".concat(this.supervisorInfo.version_latest)),this.hassOsInfo?this._renderUpdateCard("Operating System",this.hassOsInfo.version,this.hassOsInfo.version_latest,"hassio/os/update","https://github.com//home-assistant/hassos/releases/tag/".concat(this.hassOsInfo.version_latest)):""):Object(n.e)(xe())}},{kind:"method",key:"_renderUpdateCard",value:function(e,t,r,i,o,a){return r&&r!==t?Object(n.e)(Ee(),a?Object(n.e)(ke(),a):"",e,r,t,o,this.hass,i,this._apiCalled):Object(n.e)(Oe())}},{kind:"method",key:"_apiCalled",value:function(e){if(e.detail.success)this._error="";else{var t=e.detail.response;"object"===we(t.body)?this._error=t.body.message||"Unknown error":this._error=t.body}}},{kind:"get",static:!0,key:"styles",value:function(){return[i.b,re.a,Object(n.c)(ge())]}}]}},n.a);function Le(e){return(Le="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Ne(){var e=He(["\n .content {\n margin: 0 auto;\n }\n "]);return Ne=function(){return e},e}function Ue(){var e=He(['\n
\n =0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n
\n \n
\n \n ']);return Ze=function(){return e},e}function et(){var e=st(['\n \n
\n You don\'t have any snapshots yet.\n
\n
\n ']);return et=function(){return e},e}function tt(){var e=st(['\n

',"

\n "]);return tt=function(){return e},e}function rt(){var e=st(['\n \n

\n Create snapshot\n

\n

\n Snapshots allow you to easily backup and restore all data of your Home\n Assistant instance.\n

\n
\n \n
\n \n Type:\n \n \n Full snapshot\n \n \n Partial snapshot\n \n \n ','\n Security:\n \n
\n \n Create\n \n
\n \n
\n\n

Available snapshots

\n
\n ',"\n
\n
\n "]);return at=function(){return e},e}function st(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function ct(e,t,r,n,i,o,a){try{var s=e[o](a),c=s.value}catch(l){return void r(l)}s.done?t(c):Promise.resolve(c).then(n,i)}function lt(e){return function(){var t=this,r=arguments;return new Promise(function(n,i){var o=e.apply(t,r);function a(e){ct(o,n,i,a,s,"next",e)}function s(e){ct(o,n,i,a,s,"throw",e)}a(void 0)})}}function ut(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function dt(e,t){return(dt=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function ft(e){var t,r=vt(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function pt(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function ht(e){return e.decorators&&e.decorators.length}function mt(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function yt(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function vt(e){var t=function(e,t){if("object"!==Qe(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==Qe(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===Qe(t)?t:String(t)}function bt(e,t,r){return(bt="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,r){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=gt(e)););return e}(e,t);if(n){var i=Object.getOwnPropertyDescriptor(n,t);return i.get?i.get.call(r):i.value}})(e,t,r||e)}function gt(e){return(gt=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!ht(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n \n

\n ','\n

\n

\n Maintained by ','
\n \n ','\n \n

\n
\n ',"\n
\n
\n "]);return jt=function(){return e},e}function xt(){var e=_t(['\n
\n

\n No results found in "','"\n

\n
\n ']);return xt=function(){return e},e}function _t(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function St(e){return(St=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function Ct(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Dt(e,t){return(Dt=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Tt(e){var t,r=Ft(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function At(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function zt(e){return e.decorators&&e.decorators.length}function Rt(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function It(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function Ft(e){var t=function(e,t){if("object"!==kt(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==kt(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===kt(t)?t:String(t)}var Lt=function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!zt(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n
\n \n
\n
\n \n Remove\n \n
\n \n ']);return Bt=function(){return e},e}function Mt(){var e=Gt(['\n
\n

\n Repositories\n

\n

\n Configure which add-on repositories to fetch data from:\n

\n
\n ','\n\n \n
\n \n \n
\n
\n =0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n "]);return nr=function(){return e},e}function ir(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function or(e){return(or=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function ar(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function sr(e,t){return(sr=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function cr(e){var t,r=pr(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function lr(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function ur(e){return e.decorators&&e.decorators.length}function dr(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function fr(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function pr(e){var t=function(e,t){if("object"!==tr(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==tr(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===tr(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!ur(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n ']);return br=function(){return e},e}function gr(){var e=wr(['\n
\n \n \n ',"\n \n
\n "]);return gr=function(){return e},e}function wr(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function kr(e){return(kr=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function Er(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Or(e,t){return(Or=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Pr(e){var t,r=Cr(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function jr(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function xr(e){return e.decorators&&e.decorators.length}function _r(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function Sr(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function Cr(e){var t=function(e,t){if("object"!==hr(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==hr(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===hr(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!xr(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n "]);return Ar=function(){return e},e}function zr(){var e=Ir(["\n \n\n \n\n ","\n "]);return zr=function(){return e},e}function Rr(){var e=Ir(["\n \n "]);return Rr=function(){return e},e}function Ir(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Fr(e,t,r,n,i,o,a){try{var s=e[o](a),c=s.value}catch(l){return void r(l)}s.done?t(c):Promise.resolve(c).then(n,i)}function Lr(e){return function(){var t=this,r=arguments;return new Promise(function(n,i){var o=e.apply(t,r);function a(e){Fr(o,n,i,a,s,"next",e)}function s(e){Fr(o,n,i,a,s,"throw",e)}a(void 0)})}}function Nr(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Ur(e,t){return(Ur=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Hr(e){var t,r=qr(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function Br(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function Mr(e){return e.decorators&&e.decorators.length}function Gr(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function Vr(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function qr(e){var t=function(e,t){if("object"!==Dr(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==Dr(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===Dr(t)?t:String(t)}function Wr(e,t,r){return(Wr="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,r){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=Jr(e)););return e}(e,t);if(n){var i=Object.getOwnPropertyDescriptor(n,t);return i.get?i.get.call(r):i.value}})(e,t,r||e)}function Jr(e){return(Jr=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var Yr=function(e,t){return"local"===e.slug?-1:"local"===t.slug?1:"core"===e.slug?-1:"core"===t.slug?1:e.name.toUpperCase()=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;aUpdate
\n ']);return tn=function(){return e},e}function rn(){var e=un(['\n Import from USB\n ']);return rn=function(){return e},e}function nn(){var e=un(['\n Shutdown\n ']);return nn=function(){return e},e}function on(){var e=un(['\n Reboot\n ']);return on=function(){return e},e}function an(){var e=un(['\n
Error: ',"
\n "]);return an=function(){return e},e}function sn(){var e=un(["\n \n Change hostname\n \n ']);return sn=function(){return e},e}function cn(){var e=un(["\n \n Deployment\n ","\n \n "]);return cn=function(){return e},e}function ln(){var e=un(['\n \n
\n

Host system

\n \n \n \n \n \n \n \n \n \n \n ","\n \n
Hostname',"
System","
\n \n Hardware\n \n ',"\n ",'\n
\n
\n ',"\n ","\n ","\n ","\n
\n
\n "]);return ln=function(){return e},e}function un(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function dn(e){return(dn=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function fn(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function pn(e,t){return(pn=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function hn(e){var t,r=gn(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function mn(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function yn(e){return e.decorators&&e.decorators.length}function vn(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function bn(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function gn(e){var t=function(e,t){if("object"!==Zr(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==Zr(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===Zr(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!yn(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a1&&void 0!==arguments[1]?arguments[1]:"",n="";return Object.keys(e).forEach(function(i){"object"!==Zr(e[i])?n+="".concat(r,"- ").concat(i,": ").concat(e[i],"\n"):(n+="".concat(r,"- ").concat(i,":\n"),Array.isArray(e[i])?e[i].length&&(n+="".concat(r," - ")+e[i].join("\n".concat(r," - "))+"\n"):n+=t._objectToMarkdown(e[i]," ".concat(r)))}),n}},{kind:"method",key:"_changeHostnameClicked",value:function(){var e=this.hostInfo.hostname,t=prompt("Please enter a new hostname:",e);t&&t!==e&&this.hass.callApi("POST","hassio/host/options",{hostname:t})}}]}},n.a);function wn(e,t,r,n,i,o,a){try{var s=e[o](a),c=s.value}catch(l){return void r(l)}s.done?t(c):Promise.resolve(c).then(n,i)}function kn(e){return(kn="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function En(){var e=Cn(["\n paper-card {\n height: 100%;\n width: 100%;\n }\n .card-content {\n color: var(--primary-text-color);\n box-sizing: border-box;\n height: calc(100% - 47px);\n }\n .info {\n width: 100%;\n }\n .info td:nth-child(2) {\n text-align: right;\n }\n .errors {\n color: var(--google-red-500);\n margin-top: 16px;\n }\n "]);return En=function(){return e},e}function On(){var e=Cn(["\n Join beta channel\n ']);return On=function(){return e},e}function Pn(){var e=Cn(["\n Update\n ']);return jn=function(){return e},e}function xn(){var e=Cn(['\n
Error: ',"
\n "]);return xn=function(){return e},e}function _n(){var e=Cn(["\n \n Channel\n ","\n \n "]);return _n=function(){return e},e}function Sn(){var e=Cn(['\n \n
\n

Supervisor

\n \n \n \n \n \n \n \n \n \n \n ","\n \n
Version',"
Latest version","
\n ",'\n
\n
\n Reload\n ',"\n ","\n ","\n
\n
\n "]);return Sn=function(){return e},e}function Cn(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function Dn(e){return(Dn=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function Tn(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function An(e,t){return(An=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function zn(e){var t,r=Nn(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function Rn(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function In(e){return e.decorators&&e.decorators.length}function Fn(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function Ln(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function Nn(e){var t=function(e,t){if("object"!==kn(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==kn(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===kn(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!In(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a',"
\n "]);return Mn=function(){return e},e}function Gn(){var e=Vn(["\n \n ",'\n
\n
\n =0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n

Information

\n
\n =0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n \n \n \n \n Dashboard\n Snapshots\n Add-on store\n System\n \n \n =0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a200?n.scrollTop=0:t._currentAnimationId===i&&(n.scrollTop=(r=c,-s*(r/=200)*(r-2)+a),requestAnimationFrame(e.bind(t)))}.call(t)}},{kind:"method",key:"refreshClicked",value:function(){"snapshots"===this._page?this.shadowRoot.querySelector("hassio-snapshots").refreshData():this.shadowRoot.querySelector("hassio-addon-store").refreshData()}},{kind:"get",key:"_page",value:function(){return this.route.path.substr(1)}},{kind:"get",static:!0,key:"styles",value:function(){return[i.b,Object(n.c)(_i())]}}]}},n.a),r(121));function Mi(e){return(Mi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Gi(e,t){return to(e)||function(e,t){if(!(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e)))return;var r=[],n=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(n=(a=s.next()).done)&&(r.push(a.value),!t||r.length!==t);n=!0);}catch(c){i=!0,o=c}finally{try{n||null==s.return||s.return()}finally{if(i)throw o}}return r}(e,t)||eo()}function Vi(e,t,r,n,i,o,a){try{var s=e[o](a),c=s.value}catch(l){return void r(l)}s.done?t(c):Promise.resolve(c).then(n,i)}function qi(e){return function(){var t=this,r=arguments;return new Promise(function(n,i){var o=e.apply(t,r);function a(e){Vi(o,n,i,a,s,"next",e)}function s(e){Vi(o,n,i,a,s,"throw",e)}a(void 0)})}}function Wi(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function Ji(e,t){return(Ji=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function Yi(e){var t,r=Zi(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function $i(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function Ki(e){return e.decorators&&e.decorators.length}function Qi(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function Xi(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function Zi(e){var t=function(e,t){if("object"!==Mi(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==Mi(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===Mi(t)?t:String(t)}function eo(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function to(e){if(Array.isArray(e))return e}function ro(e,t,r){return(ro="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,r){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=no(e)););return e}(e,t);if(n){var i=Object.getOwnPropertyDescriptor(n,t);return i.get?i.get.call(r):i.value}})(e,t,r||e)}function no(e){return(no=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}customElements.get("paper-icon-button").prototype._keyBindings={};var io;!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!Ki(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a2&&void 0!==arguments[2]&&arguments[2];r?history.replaceState(null,"",t):history.pushState(null,"",t),Object(n.a)(window,"location-changed",{replace:r})}},41:function(e,t,r){"use strict";var n=r(5),i=(r(22),r(8)),o=r(34),a=[60,60,24,7],s=["second","minute","hour","day"];var c=r(17);function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function u(e,t){for(var r=0;r2&&void 0!==arguments[2]?arguments[2]:{},i=((n.compareTime||new Date).getTime()-e.getTime())/1e3,o=i>=0?"past":"future";i=Math.abs(i);for(var c=0;c\n \n
\n
\n ']);return _=function(){return e},e}function S(){var e=D(['\n
\n ']);return S=function(){return e},e}function C(){var e=D(["\n ","\n ",'\n
\n
\n ','\n
\n
\n ',"\n ","\n ","\n
\n
\n "]);return C=function(){return e},e}function D(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function T(e){return(T=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function A(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function z(e,t){return(z=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function R(e){var t,r=U(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function I(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function F(e){return e.decorators&&e.decorators.length}function L(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function N(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function U(e){var t=function(e,t){if("object"!==O(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==O(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===O(t)?t:String(t)}customElements.define("ha-relative-time",E);!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!F(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n ']);return s=function(){return e},e}function c(e,t){return!t||"object"!==o(t)&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}function l(e){return(l=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function u(e,t){for(var r=0;rt||Number(o)===t&&Number(a)>=r}},92:function(e,t,r){"use strict";r(24);var n=r(5),i=r(11),o=r(180),a=function(e){return e.sendMessagePromise({type:"persistent_notification/get"})},s=function(e,t){return e.subscribeEvents(function(){return a(e).then(function(e){return t.setState(e,!0)})},"persistent_notifications_updated")};function c(e){return(c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function l(){var e=f(["\n :host {\n position: relative;\n }\n .dot {\n pointer-events: none;\n position: absolute;\n background-color: var(--accent-color);\n width: 12px;\n height: 12px;\n top: 5px;\n right: 2px;\n border-radius: 50%;\n border: 2px solid var(--app-header-background-color);\n }\n "]);return l=function(){return e},e}function u(){var e=f(['\n
\n ']);return u=function(){return e},e}function d(){var e=f(["\n \n ","\n "]);return d=function(){return e},e}function f(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function p(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function h(e,t){return(h=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function m(e){var t,r=w(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function y(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function v(e){return e.decorators&&e.decorators.length}function b(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function g(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function w(e){var t=function(e,t){if("object"!==c(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==c(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===c(t)?t:String(t)}function k(e,t,r){return(k="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,r){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=E(e)););return e}(e,t);if(n){var i=Object.getOwnPropertyDescriptor(n,t);return i.get?i.get.call(r):i.value}})(e,t,r||e)}function E(e){return(E=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!v(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var u=0;u=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a0},Object(o.a)("_ntf",a,s,e,t))}},{kind:"method",key:"_toggleMenu",value:function(){Object(i.a)(this,"hass-toggle-menu")}},{kind:"get",static:!0,key:"styles",value:function(){return Object(n.c)(l())}}]}},n.a)}}]); +//# sourceMappingURL=chunk.05403bc1774dc39117f6.js.map \ No newline at end of file diff --git a/supervisor/api/panel/chunk.05403bc1774dc39117f6.js.gz b/supervisor/api/panel/chunk.05403bc1774dc39117f6.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..bc15662905987a80b77578794aff19e9393ab3b3 GIT binary patch literal 32131 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\n \n\n

${this.header}
\n \n
\n
\n `;\n }\n\n private _backTapped(): void {\n history.back();\n }\n\n static get styles(): CSSResult {\n return css`\n :host {\n display: block;\n height: 100%;\n background-color: var(--primary-background-color);\n }\n\n .toolbar {\n display: flex;\n align-items: center;\n font-size: 20px;\n height: 65px;\n padding: 0 16px;\n pointer-events: none;\n background-color: var(--app-header-background-color);\n font-weight: 400;\n color: var(--app-header-text-color, white);\n border-bottom: var(--app-header-border-bottom, none);\n box-sizing: border-box;\n }\n\n ha-menu-button,\n ha-paper-icon-button-arrow-prev,\n ::slotted([slot=\"toolbar-icon\"]) {\n pointer-events: auto;\n }\n\n ha-paper-icon-button-arrow-prev.hidden {\n visibility: hidden;\n }\n\n [main-title] {\n margin: 0 0 0 24px;\n line-height: 20px;\n flex-grow: 1;\n }\n\n .content {\n position: relative;\n width: 100%;\n height: calc(100% - 65px);\n overflow-y: auto;\n overflow: auto;\n -webkit-overflow-scrolling: touch;\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hass-subpage\": HassSubpage;\n }\n}\n","import \"@polymer/paper-icon-button/paper-icon-button\";\nimport { Constructor } from \"../types\";\n// Not duplicate, this is for typing.\n// tslint:disable-next-line\nimport { PaperIconButtonElement } from \"@polymer/paper-icon-button/paper-icon-button\";\n\nconst paperIconButtonClass = customElements.get(\n \"paper-icon-button\"\n) as Constructor;\n\nexport class HaPaperIconButtonArrowPrev extends paperIconButtonClass {\n public hassio?: boolean;\n\n public connectedCallback() {\n super.connectedCallback();\n\n // wait to check for direction since otherwise direction is wrong even though top level is RTL\n setTimeout(() => {\n this.icon =\n window.getComputedStyle(this).direction === \"ltr\"\n ? this.hassio\n ? \"hassio:arrow-left\"\n : \"hass:arrow-left\"\n : this.hassio\n ? \"hassio:arrow-right\"\n : \"hass:arrow-right\";\n }, 100);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ha-paper-icon-button-arrow-prev\": HaPaperIconButtonArrowPrev;\n }\n}\n\ncustomElements.define(\n \"ha-paper-icon-button-arrow-prev\",\n HaPaperIconButtonArrowPrev\n);\n","import \"@polymer/app-layout/app-toolbar/app-toolbar\";\nimport \"@polymer/paper-spinner/paper-spinner-lite\";\nimport {\n LitElement,\n TemplateResult,\n html,\n CSSResultArray,\n css,\n customElement,\n property,\n} from \"lit-element\";\nimport \"../components/ha-menu-button\";\nimport \"../components/ha-paper-icon-button-arrow-prev\";\nimport { haStyle } from \"../resources/styles\";\nimport { HomeAssistant } from \"../types\";\n\n@customElement(\"hass-loading-screen\")\nclass HassLoadingScreen extends LitElement {\n @property({ type: Boolean }) public rootnav? = false;\n @property() public hass?: HomeAssistant;\n @property() public narrow?: boolean;\n\n protected render(): TemplateResult {\n return html`\n \n ${this.rootnav\n ? html`\n \n `\n : html`\n \n `}\n \n
\n \n
\n `;\n }\n\n private _handleBack() {\n history.back();\n }\n\n static get styles(): CSSResultArray {\n return [\n haStyle,\n css`\n :host {\n display: block;\n height: 100%;\n background-color: var(--primary-background-color);\n }\n .content {\n height: calc(100% - 64px);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hass-loading-screen\": HassLoadingScreen;\n }\n}\n","// Polymer legacy event helpers used courtesy of the Polymer project.\n//\n// Copyright (c) 2017 The Polymer Authors. All rights reserved.\n//\n// Redistribution and use in source and binary forms, with or without\n// modification, are permitted provided that the following conditions are\n// met:\n//\n// * Redistributions of source code must retain the above copyright\n// notice, this list of conditions and the following disclaimer.\n// * Redistributions in binary form must reproduce the above\n// copyright notice, this list of conditions and the following disclaimer\n// in the documentation and/or other materials provided with the\n// distribution.\n// * Neither the name of Google Inc. nor the names of its\n// contributors may be used to endorse or promote products derived from\n// this software without specific prior written permission.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n// \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\ndeclare global {\n // tslint:disable-next-line\n interface HASSDomEvents {}\n}\n\nexport type ValidHassDomEvent = keyof HASSDomEvents;\n\nexport interface HASSDomEvent extends Event {\n detail: T;\n}\n\n/**\n * Dispatches a custom event with an optional detail value.\n *\n * @param {string} type Name of event type.\n * @param {*=} detail Detail value containing event-specific\n * payload.\n * @param {{ bubbles: (boolean|undefined),\n * cancelable: (boolean|undefined),\n * composed: (boolean|undefined) }=}\n * options Object specifying options. These may include:\n * `bubbles` (boolean, defaults to `true`),\n * `cancelable` (boolean, defaults to false), and\n * `node` on which to fire the event (HTMLElement, defaults to `this`).\n * @return {Event} The new event that was fired.\n */\nexport const fireEvent = (\n node: HTMLElement | Window,\n type: HassEvent,\n detail?: HASSDomEvents[HassEvent],\n options?: {\n bubbles?: boolean;\n cancelable?: boolean;\n composed?: boolean;\n }\n) => {\n options = options || {};\n // @ts-ignore\n detail = detail === null || detail === undefined ? {} : detail;\n const event = new Event(type, {\n bubbles: options.bubbles === undefined ? true : options.bubbles,\n cancelable: Boolean(options.cancelable),\n composed: options.composed === undefined ? true : options.composed,\n });\n (event as any).detail = detail;\n node.dispatchEvent(event);\n return event;\n};\n","import \"@material/mwc-button\";\nimport \"@polymer/paper-spinner/paper-spinner\";\nimport { html } from \"@polymer/polymer/lib/utils/html-tag\";\nimport { PolymerElement } from \"@polymer/polymer/polymer-element\";\n\nclass HaProgressButton extends PolymerElement {\n static get template() {\n return html`\n \n
\n \n \n \n \n
\n `;\n }\n\n static get properties() {\n return {\n hass: {\n type: Object,\n },\n\n progress: {\n type: Boolean,\n value: false,\n },\n\n disabled: {\n type: Boolean,\n value: false,\n },\n };\n }\n\n tempClass(className) {\n var classList = this.$.container.classList;\n classList.add(className);\n setTimeout(() => {\n classList.remove(className);\n }, 1000);\n }\n\n ready() {\n super.ready();\n this.addEventListener(\"click\", (ev) => this.buttonTapped(ev));\n }\n\n buttonTapped(ev) {\n if (this.progress) ev.stopPropagation();\n }\n\n actionSuccess() {\n this.tempClass(\"success\");\n }\n\n actionError() {\n this.tempClass(\"error\");\n }\n\n computeDisabled(disabled, progress) {\n return disabled || progress;\n }\n}\n\ncustomElements.define(\"ha-progress-button\", HaProgressButton);\n","import { HomeAssistant } from \"../../types\";\nimport { HassioResponse, hassioApiResultExtractor } from \"./common\";\n\nexport interface HassioSnapshot {\n slug: string;\n date: string;\n name: string;\n type: \"full\" | \"partial\";\n protected: boolean;\n}\n\nexport interface HassioSnapshotDetail extends HassioSnapshot {\n size: number;\n homeassistant: string;\n addons: Array<{\n slug: \"ADDON_SLUG\";\n name: \"NAME\";\n version: \"INSTALLED_VERSION\";\n size: \"SIZE_IN_MB\";\n }>;\n repositories: string[];\n folders: string[];\n}\n\nexport interface HassioFullSnapshotCreateParams {\n name: string;\n password?: string;\n}\nexport interface HassioPartialSnapshotCreateParams {\n name: string;\n folders: string[];\n addons: string[];\n password?: string;\n}\n\nexport const fetchHassioSnapshots = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n \"hassio/snapshots\"\n )\n ).snapshots;\n};\n\nexport const fetchHassioSnapshotInfo = async (\n hass: HomeAssistant,\n snapshot: string\n) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n `hassio/snapshots/${snapshot}/info`\n )\n );\n};\n\nexport const reloadHassioSnapshots = async (hass: HomeAssistant) => {\n await hass.callApi>(\"POST\", `hassio/snapshots/reload`);\n};\n\nexport const createHassioFullSnapshot = async (\n hass: HomeAssistant,\n data: HassioFullSnapshotCreateParams\n) => {\n await hass.callApi>(\n \"POST\",\n `hassio/snapshots/new/full`,\n data\n );\n};\n\nexport const createHassioPartialSnapshot = async (\n hass: HomeAssistant,\n data: HassioFullSnapshotCreateParams\n) => {\n await hass.callApi>(\n \"POST\",\n `hassio/snapshots/new/partial`,\n data\n );\n};\n","import { HomeAssistant } from \"../../types\";\nimport { HassioResponse, hassioApiResultExtractor } from \"./common\";\n\nexport interface HassioHardwareAudioDevice {\n device?: string | null;\n name: string;\n}\n\ninterface HassioHardwareAudioList {\n audio: {\n input: { [key: string]: string };\n output: { [key: string]: string };\n };\n}\n\nexport interface HassioHardwareInfo {\n serial: string[];\n input: string[];\n disk: string[];\n gpio: string[];\n audio: object;\n}\n\nexport const fetchHassioHardwareAudio = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n \"hassio/hardware/audio\"\n )\n );\n};\n\nexport const fetchHassioHardwareInfo = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n \"hassio/hardware/info\"\n )\n );\n};\n","import { fireEvent } from \"../../../../src/common/dom/fire_event\";\n\nexport interface HassioMarkdownDialogParams {\n title: string;\n content: string;\n}\n\nexport const showHassioMarkdownDialog = (\n element: HTMLElement,\n dialogParams: HassioMarkdownDialogParams\n): void => {\n fireEvent(element, \"show-dialog\", {\n dialogTag: \"dialog-hassio-markdown\",\n dialogImport: () =>\n import(\n /* webpackChunkName: \"dialog-hassio-markdown\" */ \"./dialog-hassio-markdown\"\n ),\n dialogParams,\n });\n};\n","import { css } from \"lit-element\";\n\ninterface State {\n bold: boolean;\n italic: boolean;\n underline: boolean;\n strikethrough: boolean;\n foregroundColor: null | string;\n backgroundColor: null | string;\n}\n\nexport const ANSI_HTML_STYLE = css`\n .bold {\n font-weight: bold;\n }\n .italic {\n font-style: italic;\n }\n .underline {\n text-decoration: underline;\n }\n .strikethrough {\n text-decoration: line-through;\n }\n .underline.strikethrough {\n text-decoration: underline line-through;\n }\n .fg-red {\n color: rgb(222, 56, 43);\n }\n .fg-green {\n color: rgb(57, 181, 74);\n }\n .fg-yellow {\n color: rgb(255, 199, 6);\n }\n .fg-blue {\n color: rgb(0, 111, 184);\n }\n .fg-magenta {\n color: rgb(118, 38, 113);\n }\n .fg-cyan {\n color: rgb(44, 181, 233);\n }\n .fg-white {\n color: rgb(204, 204, 204);\n }\n .bg-black {\n background-color: rgb(0, 0, 0);\n }\n .bg-red {\n background-color: rgb(222, 56, 43);\n }\n .bg-green {\n background-color: rgb(57, 181, 74);\n }\n .bg-yellow {\n background-color: rgb(255, 199, 6);\n }\n .bg-blue {\n background-color: rgb(0, 111, 184);\n }\n .bg-magenta {\n background-color: rgb(118, 38, 113);\n }\n .bg-cyan {\n background-color: rgb(44, 181, 233);\n }\n .bg-white {\n background-color: rgb(204, 204, 204);\n }\n`;\n\nexport function parseTextToColoredPre(text) {\n const pre = document.createElement(\"pre\");\n const re = /\\033(?:\\[(.*?)[@-~]|\\].*?(?:\\007|\\033\\\\))/g;\n let i = 0;\n\n const state: State = {\n bold: false,\n italic: false,\n underline: false,\n strikethrough: false,\n foregroundColor: null,\n backgroundColor: null,\n };\n\n const addSpan = (content) => {\n const span = document.createElement(\"span\");\n if (state.bold) {\n span.classList.add(\"bold\");\n }\n if (state.italic) {\n span.classList.add(\"italic\");\n }\n if (state.underline) {\n span.classList.add(\"underline\");\n }\n if (state.strikethrough) {\n span.classList.add(\"strikethrough\");\n }\n if (state.foregroundColor !== null) {\n span.classList.add(`fg-${state.foregroundColor}`);\n }\n if (state.backgroundColor !== null) {\n span.classList.add(`bg-${state.backgroundColor}`);\n }\n span.appendChild(document.createTextNode(content));\n pre.appendChild(span);\n };\n\n /* eslint-disable no-cond-assign */\n let match;\n // tslint:disable-next-line\n while ((match = re.exec(text)) !== null) {\n const j = match!.index;\n addSpan(text.substring(i, j));\n i = j + match[0].length;\n\n if (match[1] === undefined) {\n continue;\n }\n\n match[1].split(\";\").forEach((colorCode: string) => {\n switch (parseInt(colorCode, 10)) {\n case 0:\n // reset\n state.bold = false;\n state.italic = false;\n state.underline = false;\n state.strikethrough = false;\n state.foregroundColor = null;\n state.backgroundColor = null;\n break;\n case 1:\n state.bold = true;\n break;\n case 3:\n state.italic = true;\n break;\n case 4:\n state.underline = true;\n break;\n case 9:\n state.strikethrough = true;\n break;\n case 22:\n state.bold = false;\n break;\n case 23:\n state.italic = false;\n break;\n case 24:\n state.underline = false;\n break;\n case 29:\n state.strikethrough = false;\n break;\n case 30:\n // foreground black\n state.foregroundColor = null;\n break;\n case 31:\n state.foregroundColor = \"red\";\n break;\n case 32:\n state.foregroundColor = \"green\";\n break;\n case 33:\n state.foregroundColor = \"yellow\";\n break;\n case 34:\n state.foregroundColor = \"blue\";\n break;\n case 35:\n state.foregroundColor = \"magenta\";\n break;\n case 36:\n state.foregroundColor = \"cyan\";\n break;\n case 37:\n state.foregroundColor = \"white\";\n break;\n case 39:\n // foreground reset\n state.foregroundColor = null;\n break;\n case 40:\n state.backgroundColor = \"black\";\n break;\n case 41:\n state.backgroundColor = \"red\";\n break;\n case 42:\n state.backgroundColor = \"green\";\n break;\n case 43:\n state.backgroundColor = \"yellow\";\n break;\n case 44:\n state.backgroundColor = \"blue\";\n break;\n case 45:\n state.backgroundColor = \"magenta\";\n break;\n case 46:\n state.backgroundColor = \"cyan\";\n break;\n case 47:\n state.backgroundColor = \"white\";\n break;\n case 49:\n // background reset\n state.backgroundColor = null;\n break;\n }\n });\n }\n addSpan(text.substring(i));\n\n return pre;\n}\n","import { fireEvent } from \"../../common/dom/fire_event\";\nimport { TemplateResult } from \"lit-html\";\n\ninterface BaseDialogParams {\n confirmText?: string;\n text?: string | TemplateResult;\n title?: string;\n}\n\nexport interface AlertDialogParams extends BaseDialogParams {\n confirm?: () => void;\n}\n\nexport interface ConfirmationDialogParams extends BaseDialogParams {\n dismissText?: string;\n confirm?: () => void;\n cancel?: () => void;\n}\n\nexport interface PromptDialogParams extends BaseDialogParams {\n inputLabel?: string;\n inputType?: string;\n defaultValue?: string;\n confirm?: (out?: string) => void;\n}\n\nexport interface DialogParams\n extends ConfirmationDialogParams,\n PromptDialogParams {\n confirm?: (out?: string) => void;\n confirmation?: boolean;\n prompt?: boolean;\n}\n\nexport const loadGenericDialog = () =>\n import(/* webpackChunkName: \"confirmation\" */ \"./dialog-box\");\n\nconst showDialogHelper = (\n element: HTMLElement,\n dialogParams: DialogParams,\n extra?: {\n confirmation?: DialogParams[\"confirmation\"];\n prompt?: DialogParams[\"prompt\"];\n }\n) =>\n new Promise((resolve) => {\n const origCancel = dialogParams.cancel;\n const origConfirm = dialogParams.confirm;\n\n fireEvent(element, \"show-dialog\", {\n dialogTag: \"dialog-box\",\n dialogImport: loadGenericDialog,\n dialogParams: {\n ...dialogParams,\n ...extra,\n cancel: () => {\n resolve(extra?.prompt ? null : false);\n if (origCancel) {\n origCancel();\n }\n },\n confirm: (out) => {\n resolve(extra?.prompt ? out : true);\n if (origConfirm) {\n origConfirm(out);\n }\n },\n },\n });\n });\n\nexport const showAlertDialog = (\n element: HTMLElement,\n dialogParams: AlertDialogParams\n) => showDialogHelper(element, dialogParams);\n\nexport const showConfirmationDialog = (\n element: HTMLElement,\n dialogParams: ConfirmationDialogParams\n) =>\n showDialogHelper(element, dialogParams, { confirmation: true }) as Promise<\n boolean\n >;\n\nexport const showPromptDialog = (\n element: HTMLElement,\n dialogParams: PromptDialogParams\n) =>\n showDialogHelper(element, dialogParams, { prompt: true }) as Promise<\n null | string\n >;\n","import { css } from \"lit-element\";\n\nexport const hassioStyle = css`\n .content {\n margin: 8px;\n }\n h1 {\n color: var(--primary-text-color);\n font-size: 2em;\n margin-bottom: 8px;\n font-family: var(--paper-font-headline_-_font-family);\n -webkit-font-smoothing: var(--paper-font-headline_-_-webkit-font-smoothing);\n font-size: var(--paper-font-headline_-_font-size);\n font-weight: var(--paper-font-headline_-_font-weight);\n letter-spacing: var(--paper-font-headline_-_letter-spacing);\n line-height: var(--paper-font-headline_-_line-height);\n padding-left: 8px;\n }\n .description {\n margin-top: 4px;\n padding-left: 8px;\n }\n .card-group {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n grid-gap: 8px;\n }\n @media screen and (min-width: 640px) {\n .card-group {\n grid-template-columns: repeat(auto-fit, minmax(300px, 0.5fr));\n }\n }\n @media screen and (min-width: 1020px) {\n .card-group {\n grid-template-columns: repeat(auto-fit, minmax(300px, 0.333fr));\n }\n }\n @media screen and (min-width: 1300px) {\n .card-group {\n grid-template-columns: repeat(auto-fit, minmax(300px, 0.25fr));\n }\n }\n ha-call-api-button {\n font-weight: 500;\n color: var(--primary-color);\n }\n .error {\n color: var(--error-color);\n margin-top: 16px;\n }\n`;\n","import \"@polymer/paper-styles/paper-styles\";\nimport \"@polymer/polymer/lib/elements/custom-style\";\nimport { haStyle, haStyleDialog, derivedStyles } from \"./styles\";\n\nconst documentContainer = document.createElement(\"template\");\ndocumentContainer.setAttribute(\"style\", \"display: none;\");\n\ndocumentContainer.innerHTML = `\n \n\n \n\n \n\n \n`;\n\ndocument.head.appendChild(documentContainer.content);\n","import { derivedStyles } from \"../../resources/styles\";\nimport { HomeAssistant, Theme } from \"../../types\";\n\ninterface ProcessedTheme {\n keys: { [key: string]: \"\" };\n styles: { [key: string]: string };\n}\n\nconst hexToRgb = (hex: string): string | null => {\n const shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i;\n const checkHex = hex.replace(shorthandRegex, (_m, r, g, b) => {\n return r + r + g + g + b + b;\n });\n\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(checkHex);\n return result\n ? `${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(\n result[3],\n 16\n )}`\n : null;\n};\n\nlet PROCESSED_THEMES: { [key: string]: ProcessedTheme } = {};\n\n/**\n * Apply a theme to an element by setting the CSS variables on it.\n *\n * element: Element to apply theme on.\n * themes: HASS Theme information\n * selectedTheme: selected theme.\n */\nexport const applyThemesOnElement = (\n element,\n themes: HomeAssistant[\"themes\"],\n selectedTheme?: string\n) => {\n const newTheme = selectedTheme\n ? PROCESSED_THEMES[selectedTheme] || processTheme(selectedTheme, themes)\n : undefined;\n\n if (!element._themes && !newTheme) {\n // No styles to reset, and no styles to set\n return;\n }\n\n // Add previous set keys to reset them, and new theme\n const styles = { ...element._themes, ...newTheme?.styles };\n element._themes = newTheme?.keys;\n\n // Set and/or reset styles\n if (element.updateStyles) {\n element.updateStyles(styles);\n } else if (window.ShadyCSS) {\n // Implement updateStyles() method of Polymer elements\n window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */ element, styles);\n }\n};\n\nconst processTheme = (\n themeName: string,\n themes: HomeAssistant[\"themes\"]\n): ProcessedTheme | undefined => {\n if (!themes.themes[themeName]) {\n return;\n }\n const theme: Theme = {\n ...derivedStyles,\n ...themes.themes[themeName],\n };\n const styles = {};\n const keys = {};\n for (const key of Object.keys(theme)) {\n const prefixedKey = `--${key}`;\n const value = theme[key];\n styles[prefixedKey] = value;\n keys[prefixedKey] = \"\";\n\n // Try to create a rgb value for this key if it is a hex color\n if (!value.startsWith(\"#\")) {\n // Not a hex color\n continue;\n }\n const rgbKey = `rgb-${key}`;\n if (theme[rgbKey] !== undefined) {\n // Theme has it's own rgb value\n continue;\n }\n const rgbValue = hexToRgb(value);\n if (rgbValue !== null) {\n const prefixedRgbKey = `--${rgbKey}`;\n styles[prefixedRgbKey] = rgbValue;\n keys[prefixedRgbKey] = \"\";\n }\n }\n PROCESSED_THEMES[themeName] = { styles, keys };\n return { styles, keys };\n};\n\nexport const invalidateThemeCache = () => {\n PROCESSED_THEMES = {};\n};\n","import {\n LitElement,\n CSSResultArray,\n css,\n TemplateResult,\n html,\n property,\n customElement,\n} from \"lit-element\";\nimport \"@material/mwc-button\";\nimport \"./hass-subpage\";\n\n@customElement(\"hass-error-screen\")\nclass HassErrorScreen extends LitElement {\n @property()\n public error?: string;\n\n protected render(): TemplateResult {\n return html`\n \n
\n

${this.error}

\n \n go back\n \n
\n
\n `;\n }\n\n private _backTapped(): void {\n history.back();\n }\n\n static get styles(): CSSResultArray {\n return [\n css`\n .content {\n height: calc(100% - 64px);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hass-error-screen\": HassErrorScreen;\n }\n}\n","import { UpdatingElement, property, PropertyValues } from \"lit-element\";\nimport \"./hass-error-screen\";\nimport \"./hass-loading-screen\";\nimport { Route } from \"../types\";\nimport { navigate } from \"../common/navigate\";\nimport memoizeOne from \"memoize-one\";\n\nconst extractPage = (path: string, defaultPage: string) => {\n if (path === \"\") {\n return defaultPage;\n }\n const subpathStart = path.indexOf(\"/\", 1);\n return subpathStart === -1\n ? path.substr(1)\n : path.substr(1, subpathStart - 1);\n};\n\nexport interface RouteOptions {\n // HTML tag of the route page.\n tag: string;\n // Function to load the page.\n load?: () => Promise;\n cache?: boolean;\n}\n\nexport interface RouterOptions {\n // The default route to show if path does not define a page.\n defaultPage?: string;\n // If all routes should be preloaded\n preloadAll?: boolean;\n // If a route has been shown, should we keep the element in memory\n cacheAll?: boolean;\n // Should we show a loading spinner while we load the element for the route\n showLoading?: boolean;\n // Promise that resolves when the initial data is loaded which is needed to show any route.\n initialLoad?: () => Promise;\n // Hook that is called before rendering a new route. Allowing redirects.\n // If string returned, that page will be rendered instead.\n beforeRender?: (page: string) => string | undefined;\n routes: {\n // If it's a string, it is another route whose options should be adopted.\n [route: string]: RouteOptions | string;\n };\n}\n\n// Time to wait for code to load before we show loading screen.\nconst LOADING_SCREEN_THRESHOLD = 400; // ms\n\nexport class HassRouterPage extends UpdatingElement {\n @property() public route?: Route;\n\n protected routerOptions!: RouterOptions;\n\n protected _currentPage = \"\";\n private _currentLoadProm?: Promise;\n private _cache = {};\n private _initialLoadDone = false;\n private _computeTail = memoizeOne((route: Route) => {\n const dividerPos = route.path.indexOf(\"/\", 1);\n return dividerPos === -1\n ? {\n prefix: route.prefix + route.path,\n path: \"\",\n }\n : {\n prefix: route.prefix + route.path.substr(0, dividerPos),\n path: route.path.substr(dividerPos),\n };\n });\n\n protected update(changedProps: PropertyValues) {\n super.update(changedProps);\n\n const routerOptions = this.routerOptions || { routes: {} };\n\n if (routerOptions && routerOptions.initialLoad && !this._initialLoadDone) {\n return;\n }\n\n if (!changedProps.has(\"route\")) {\n // Do not update if we have a currentLoadProm, because that means\n // that there is still an old panel shown and we're moving to a new one.\n if (this.lastChild && !this._currentLoadProm) {\n this.updatePageEl(this.lastChild, changedProps);\n }\n return;\n }\n\n const route = this.route;\n const defaultPage = routerOptions.defaultPage;\n\n if (route && route.path === \"\" && defaultPage !== undefined) {\n navigate(this, `${route.prefix}/${defaultPage}`, true);\n }\n\n let newPage = route\n ? extractPage(route.path, defaultPage || \"\")\n : \"not_found\";\n let routeOptions = routerOptions.routes[newPage];\n\n // Handle redirects\n while (typeof routeOptions === \"string\") {\n newPage = routeOptions;\n routeOptions = routerOptions.routes[newPage];\n }\n\n if (routerOptions.beforeRender) {\n const result = routerOptions.beforeRender(newPage);\n if (result !== undefined) {\n newPage = result;\n routeOptions = routerOptions.routes[newPage];\n\n // Handle redirects\n while (typeof routeOptions === \"string\") {\n newPage = routeOptions;\n routeOptions = routerOptions.routes[newPage];\n }\n\n // Update the url if we know where we're mounted.\n if (route) {\n navigate(this, `${route.prefix}/${result}`, true);\n }\n }\n }\n\n if (this._currentPage === newPage) {\n if (this.lastChild) {\n this.updatePageEl(this.lastChild, changedProps);\n }\n return;\n }\n\n if (!routeOptions) {\n this._currentPage = \"\";\n if (this.lastChild) {\n this.removeChild(this.lastChild);\n }\n return;\n }\n\n this._currentPage = newPage;\n const loadProm = routeOptions.load\n ? routeOptions.load()\n : Promise.resolve();\n\n // Check when loading the page source failed.\n loadProm.catch((err) => {\n // tslint:disable-next-line\n console.error(\"Error loading page\", newPage, err);\n\n // Verify that we're still trying to show the same page.\n if (this._currentPage !== newPage) {\n return;\n }\n\n // Removes either loading screen or the panel\n this.removeChild(this.lastChild!);\n\n // Show error screen\n const errorEl = document.createElement(\"hass-error-screen\");\n errorEl.error = `Error while loading page ${newPage}.`;\n this.appendChild(errorEl);\n });\n\n // If we don't show loading screen, just show the panel.\n // It will be automatically upgraded when loading done.\n if (!routerOptions.showLoading) {\n this._createPanel(routerOptions, newPage, routeOptions);\n return;\n }\n\n // We are only going to show the loading screen after some time.\n // That way we won't have a double fast flash on fast connections.\n let created = false;\n\n setTimeout(() => {\n if (created || this._currentPage !== newPage) {\n return;\n }\n\n // Show a loading screen.\n if (this.lastChild) {\n this.removeChild(this.lastChild);\n }\n this.appendChild(this.createLoadingScreen());\n }, LOADING_SCREEN_THRESHOLD);\n\n this._currentLoadProm = loadProm.then(\n () => {\n this._currentLoadProm = undefined;\n // Check if we're still trying to show the same page.\n if (this._currentPage !== newPage) {\n return;\n }\n\n created = true;\n this._createPanel(\n routerOptions,\n newPage,\n // @ts-ignore TS forgot this is not a string.\n routeOptions\n );\n },\n () => {\n this._currentLoadProm = undefined;\n }\n );\n }\n\n protected firstUpdated(changedProps: PropertyValues) {\n super.firstUpdated(changedProps);\n\n const options = this.routerOptions;\n\n if (!options) {\n return;\n }\n\n if (options.preloadAll) {\n Object.values(options.routes).forEach(\n (route) => typeof route === \"object\" && route.load && route.load()\n );\n }\n\n if (options.initialLoad) {\n setTimeout(() => {\n if (!this._initialLoadDone) {\n this.appendChild(this.createLoadingScreen());\n }\n }, LOADING_SCREEN_THRESHOLD);\n\n options.initialLoad().then(() => {\n this._initialLoadDone = true;\n this.requestUpdate(\"route\");\n });\n }\n }\n\n protected createLoadingScreen() {\n return document.createElement(\"hass-loading-screen\");\n }\n\n /**\n * Rebuild the current panel.\n *\n * Promise will resolve when rebuilding is done and DOM updated.\n */\n protected async rebuild(): Promise {\n const oldRoute = this.route;\n\n if (oldRoute === undefined) {\n return;\n }\n\n this.route = undefined;\n await this.updateComplete;\n // Make sure that the parent didn't override this in the meanwhile.\n if (this.route === undefined) {\n this.route = oldRoute;\n }\n }\n\n /**\n * Promise that resolves when the page has rendered.\n */\n protected get pageRendered(): Promise {\n return this.updateComplete.then(() => this._currentLoadProm);\n }\n\n protected createElement(tag: string) {\n return document.createElement(tag);\n }\n\n protected updatePageEl(_pageEl, _changedProps?: PropertyValues) {\n // default we do nothing\n }\n\n protected get routeTail(): Route {\n return this._computeTail(this.route!);\n }\n\n private _createPanel(\n routerOptions: RouterOptions,\n page: string,\n routeOptions: RouteOptions\n ) {\n if (this.lastChild) {\n this.removeChild(this.lastChild);\n }\n\n const panelEl = this._cache[page] || this.createElement(routeOptions.tag);\n this.updatePageEl(panelEl);\n this.appendChild(panelEl);\n\n if (routerOptions.cacheAll || routeOptions.cache) {\n this._cache[page] = panelEl;\n }\n }\n}\n","import { HomeAssistant } from \"../../types\";\nimport { HassioResponse, hassioApiResultExtractor } from \"./common\";\n\nexport type HassioHostInfo = any;\n\nexport interface HassioHassOSInfo {\n version: string;\n version_cli: string;\n version_latest: string;\n version_cli_latest: string;\n board: \"ova\" | \"rpi\";\n}\n\nexport const fetchHassioHostInfo = async (hass: HomeAssistant) => {\n const response = await hass.callApi>(\n \"GET\",\n \"hassio/host/info\"\n );\n return hassioApiResultExtractor(response);\n};\n\nexport const fetchHassioHassOsInfo = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n \"hassio/os/info\"\n )\n );\n};\n","import { HASSDomEvent, ValidHassDomEvent } from \"../common/dom/fire_event\";\nimport { ProvideHassElement } from \"../mixins/provide-hass-lit-mixin\";\n\ndeclare global {\n // for fire event\n interface HASSDomEvents {\n \"show-dialog\": ShowDialogParams;\n \"close-dialog\": undefined;\n }\n // for add event listener\n interface HTMLElementEventMap {\n \"show-dialog\": HASSDomEvent>;\n }\n}\n\ninterface HassDialog extends HTMLElement {\n showDialog(params: T);\n}\n\ninterface ShowDialogParams {\n dialogTag: keyof HTMLElementTagNameMap;\n dialogImport: () => Promise;\n dialogParams: T;\n}\n\nconst LOADED = {};\n\nexport const showDialog = async (\n element: HTMLElement & ProvideHassElement,\n root: ShadowRoot | HTMLElement,\n dialogImport: () => Promise,\n dialogTag: string,\n dialogParams: unknown\n) => {\n if (!(dialogTag in LOADED)) {\n LOADED[dialogTag] = dialogImport().then(() => {\n const dialogEl = document.createElement(dialogTag) as HassDialog;\n element.provideHass(dialogEl);\n root.appendChild(dialogEl);\n return dialogEl;\n });\n }\n const dialogElement = await LOADED[dialogTag];\n dialogElement.showDialog(dialogParams);\n};\n\nexport const makeDialogManager = (\n element: HTMLElement & ProvideHassElement,\n root: ShadowRoot | HTMLElement\n) => {\n element.addEventListener(\n \"show-dialog\",\n async (e: HASSDomEvent>) => {\n const { dialogTag, dialogImport, dialogParams } = e.detail;\n showDialog(element, root, dialogImport, dialogTag, dialogParams);\n }\n );\n};\n","import { UpdatingElement, PropertyValues } from \"lit-element\";\nimport { HomeAssistant, Constructor } from \"../types\";\n\nexport interface ProvideHassElement {\n provideHass(element: HTMLElement);\n}\n\n/* tslint:disable-next-line:variable-name */\nexport const ProvideHassLitMixin = >(\n superClass: T\n) =>\n class extends superClass {\n protected hass!: HomeAssistant;\n /* tslint:disable-next-line:variable-name */\n private __provideHass: HTMLElement[] = [];\n\n public provideHass(el) {\n this.__provideHass.push(el);\n el.hass = this.hass;\n }\n\n protected updated(changedProps: PropertyValues) {\n super.updated(changedProps);\n\n if (changedProps.has(\"hass\")) {\n this.__provideHass.forEach((el) => {\n (el as any).hass = this.hass;\n });\n }\n }\n };\n","import \"@polymer/paper-card/paper-card\";\nimport {\n css,\n CSSResult,\n customElement,\n html,\n LitElement,\n property,\n TemplateResult,\n} from \"lit-element\";\n\nimport { HomeAssistant } from \"../../../src/types\";\nimport { HassioAddonInfo } from \"../../../src/data/hassio/addon\";\nimport { navigate } from \"../../../src/common/navigate\";\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\nimport \"../components/hassio-card-content\";\nimport { atLeastVersion } from \"../../../src/common/config/version\";\n\n@customElement(\"hassio-addons\")\nclass HassioAddons extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public addons?: HassioAddonInfo[];\n\n protected render(): TemplateResult {\n return html`\n
\n

Add-ons

\n
\n ${!this.addons\n ? html`\n \n
\n You don't have any add-ons installed yet. Head over to\n the add-on store\n to get started!\n
\n
\n `\n : this.addons\n .sort((a, b) => (a.name > b.name ? 1 : -1))\n .map(\n (addon) => html`\n \n
\n \n
\n
\n `\n )}\n
\n
\n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n hassioStyle,\n css`\n paper-card {\n cursor: pointer;\n }\n `,\n ];\n }\n\n private _addonTapped(ev: any): void {\n navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);\n }\n\n private _openStore(): void {\n navigate(this, \"/hassio/store\");\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-addons\": HassioAddons;\n }\n}\n","import {\n LitElement,\n TemplateResult,\n html,\n CSSResult,\n css,\n property,\n customElement,\n} from \"lit-element\";\nimport \"@polymer/iron-icon/iron-icon\";\n\nimport { HomeAssistant } from \"../../../src/types\";\nimport { HassioHassOSInfo } from \"../../../src/data/hassio/host\";\nimport {\n HassioHomeAssistantInfo,\n HassioSupervisorInfo,\n} from \"../../../src/data/hassio/supervisor\";\n\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\n\nimport \"@material/mwc-button\";\nimport \"@polymer/paper-card/paper-card\";\nimport \"../../../src/components/buttons/ha-call-api-button\";\nimport \"../components/hassio-card-content\";\n\n@customElement(\"hassio-update\")\nexport class HassioUpdate extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public hassInfo: HassioHomeAssistantInfo;\n @property() public hassOsInfo?: HassioHassOSInfo;\n @property() public supervisorInfo: HassioSupervisorInfo;\n @property() private _error?: string;\n\n protected render(): TemplateResult {\n const updatesAvailable: number = [\n this.hassInfo,\n this.supervisorInfo,\n this.hassOsInfo,\n ].filter((value) => {\n return (\n !!value &&\n (value.version_latest\n ? value.version !== value.version_latest\n : value.version_latest\n ? value.version !== value.version_latest\n : false)\n );\n }).length;\n\n if (!updatesAvailable) {\n return html``;\n }\n\n return html`\n
\n ${this._error\n ? html`\n
Error: ${this._error}
\n `\n : \"\"}\n

\n ${updatesAvailable > 1\n ? \"Updates Available 🎉\"\n : \"Update Available 🎉\"}\n

\n
\n ${this._renderUpdateCard(\n \"Home Assistant Core\",\n this.hassInfo.version,\n this.hassInfo.version_latest,\n \"hassio/homeassistant/update\",\n `https://${\n this.hassInfo.version_latest.includes(\"b\") ? \"rc\" : \"www\"\n }.home-assistant.io/latest-release-notes/`,\n \"hassio:home-assistant\"\n )}\n ${this._renderUpdateCard(\n \"Supervisor\",\n this.supervisorInfo.version,\n this.supervisorInfo.version_latest,\n \"hassio/supervisor/update\",\n `https://github.com//home-assistant/hassio/releases/tag/${this.supervisorInfo.version_latest}`\n )}\n ${this.hassOsInfo\n ? this._renderUpdateCard(\n \"Operating System\",\n this.hassOsInfo.version,\n this.hassOsInfo.version_latest,\n \"hassio/os/update\",\n `https://github.com//home-assistant/hassos/releases/tag/${this.hassOsInfo.version_latest}`\n )\n : \"\"}\n
\n
\n `;\n }\n\n private _renderUpdateCard(\n name: string,\n curVersion: string,\n lastVersion: string,\n apiPath: string,\n releaseNotesUrl: string,\n icon?: string\n ): TemplateResult {\n if (!lastVersion || lastVersion === curVersion) {\n return html``;\n }\n return html`\n \n
\n ${icon\n ? html`\n
\n \n
\n `\n : \"\"}\n
${name} ${lastVersion}
\n
\n You are currently running version ${curVersion}\n
\n
\n
\n \n Release notes\n \n \n Update\n \n
\n
\n `;\n }\n\n private _apiCalled(ev) {\n if (ev.detail.success) {\n this._error = \"\";\n return;\n }\n\n const response = ev.detail.response;\n\n typeof response.body === \"object\"\n ? (this._error = response.body.message || \"Unknown error\")\n : (this._error = response.body);\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n hassioStyle,\n css`\n .icon {\n --iron-icon-height: 48px;\n --iron-icon-width: 48px;\n float: right;\n margin: 0 0 2px 10px;\n }\n .update-heading {\n font-size: var(--paper-font-subhead_-_font-size);\n font-weight: 500;\n margin-bottom: 0.5em;\n }\n .warning {\n color: var(--secondary-text-color);\n }\n .card-content {\n height: calc(100% - 47px);\n box-sizing: border-box;\n }\n .card-actions {\n text-align: right;\n }\n .errors {\n color: var(--google-red-500);\n padding: 16px;\n }\n a {\n text-decoration: none;\n }\n `,\n ];\n }\n}\n","import {\n LitElement,\n TemplateResult,\n html,\n CSSResult,\n css,\n property,\n customElement,\n} from \"lit-element\";\nimport \"./hassio-addons\";\nimport \"./hassio-update\";\nimport { haStyle } from \"../../../src/resources/styles\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport { HassioHassOSInfo } from \"../../../src/data/hassio/host\";\nimport {\n HassioSupervisorInfo,\n HassioHomeAssistantInfo,\n} from \"../../../src/data/hassio/supervisor\";\n\n@customElement(\"hassio-dashboard\")\nclass HassioDashboard extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public supervisorInfo!: HassioSupervisorInfo;\n @property() public hassInfo!: HassioHomeAssistantInfo;\n @property() public hassOsInfo!: HassioHassOSInfo;\n\n protected render(): TemplateResult {\n return html`\n
\n \n \n
\n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n css`\n .content {\n margin: 0 auto;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-dashboard\": HassioDashboard;\n }\n}\n","import {\n LitElement,\n TemplateResult,\n html,\n CSSResultArray,\n css,\n property,\n PropertyValues,\n customElement,\n} from \"lit-element\";\nimport \"@material/mwc-button\";\nimport \"@polymer/paper-card/paper-card\";\nimport \"@polymer/paper-checkbox/paper-checkbox\";\nimport \"@polymer/paper-input/paper-input\";\nimport \"@polymer/paper-radio-button/paper-radio-button\";\nimport \"@polymer/paper-radio-group/paper-radio-group\";\n\nimport \"../components/hassio-card-content\";\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\n\nimport { showHassioSnapshotDialog } from \"../dialogs/snapshot/show-dialog-hassio-snapshot\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport {\n HassioSnapshot,\n fetchHassioSnapshots,\n reloadHassioSnapshots,\n HassioFullSnapshotCreateParams,\n HassioPartialSnapshotCreateParams,\n createHassioFullSnapshot,\n createHassioPartialSnapshot,\n} from \"../../../src/data/hassio/snapshot\";\nimport { HassioSupervisorInfo } from \"../../../src/data/hassio/supervisor\";\nimport { PolymerChangedEvent } from \"../../../src/polymer-types\";\nimport { fireEvent } from \"../../../src/common/dom/fire_event\";\n\n// Not duplicate, used for typing\n// tslint:disable-next-line\nimport { PaperInputElement } from \"@polymer/paper-input/paper-input\";\n// tslint:disable-next-line\nimport { PaperRadioGroupElement } from \"@polymer/paper-radio-group/paper-radio-group\";\n// tslint:disable-next-line\nimport { PaperCheckboxElement } from \"@polymer/paper-checkbox/paper-checkbox\";\n\ninterface CheckboxItem {\n slug: string;\n name: string;\n checked: boolean;\n}\n\n@customElement(\"hassio-snapshots\")\nclass HassioSnapshots extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public supervisorInfo!: HassioSupervisorInfo;\n @property() private _snapshotName = \"\";\n @property() private _snapshotPassword = \"\";\n @property() private _snapshotHasPassword = false;\n @property() private _snapshotType: HassioSnapshot[\"type\"] = \"full\";\n @property() private _snapshots?: HassioSnapshot[] = [];\n @property() private _addonList: CheckboxItem[] = [];\n @property() private _folderList: CheckboxItem[] = [\n {\n slug: \"homeassistant\",\n name: \"Home Assistant configuration\",\n checked: true,\n },\n { slug: \"ssl\", name: \"SSL\", checked: true },\n { slug: \"share\", name: \"Share\", checked: true },\n { slug: \"addons/local\", name: \"Local add-ons\", checked: true },\n ];\n @property() private _creatingSnapshot = false;\n @property() private _error = \"\";\n\n public async refreshData() {\n await reloadHassioSnapshots(this.hass);\n await this._updateSnapshots();\n }\n\n protected render(): TemplateResult {\n return html`\n
\n

\n Create snapshot\n

\n

\n Snapshots allow you to easily backup and restore all data of your Home\n Assistant instance.\n

\n
\n \n
\n \n Type:\n \n \n Full snapshot\n \n \n Partial snapshot\n \n \n ${this._snapshotType === \"full\"\n ? undefined\n : html`\n Folders:\n ${this._folderList.map(\n (folder, idx) => html`\n \n ${folder.name}\n \n `\n )}\n Add-ons:\n ${this._addonList.map(\n (addon, idx) => html`\n \n ${addon.name}\n \n `\n )}\n `}\n Security:\n \n Password protection\n \n ${this._snapshotHasPassword\n ? html`\n \n `\n : undefined}\n ${this._error !== \"\"\n ? html`\n

${this._error}

\n `\n : undefined}\n
\n
\n \n Create\n \n
\n
\n
\n\n

Available snapshots

\n
\n ${this._snapshots === undefined\n ? undefined\n : this._snapshots.length === 0\n ? html`\n \n
\n You don't have any snapshots yet.\n
\n
\n `\n : this._snapshots.map(\n (snapshot) => html`\n \n
\n \n
\n \n `\n )}\n
\n
\n `;\n }\n\n protected firstUpdated(changedProps: PropertyValues) {\n super.firstUpdated(changedProps);\n this._updateSnapshots();\n }\n\n protected updated(changedProps: PropertyValues) {\n if (changedProps.has(\"supervisorInfo\")) {\n this._addonList = this.supervisorInfo.addons\n .map((addon) => ({\n slug: addon.slug,\n name: addon.name,\n checked: true,\n }))\n .sort((a, b) => (a.name < b.name ? -1 : 1));\n }\n }\n\n private _handleTextValueChanged(ev: PolymerChangedEvent) {\n const input = ev.currentTarget as PaperInputElement;\n this[`_${input.name}`] = ev.detail.value;\n }\n\n private _handleCheckboxValueChanged(ev) {\n const input = ev.currentTarget as PaperCheckboxElement;\n this[`_${input.name}`] = input.checked;\n }\n\n private _handleRadioValueChanged(ev: PolymerChangedEvent) {\n const input = ev.currentTarget as PaperRadioGroupElement;\n this[`_${input.getAttribute(\"name\")}`] = ev.detail.value;\n }\n\n private _folderChecked(ev) {\n const { idx, checked } = ev.currentTarget!;\n this._folderList = this._folderList.map((folder, curIdx) =>\n curIdx === idx ? { ...folder, checked } : folder\n );\n }\n\n private _addonChecked(ev) {\n const { idx, checked } = ev.currentTarget!;\n this._addonList = this._addonList.map((addon, curIdx) =>\n curIdx === idx ? { ...addon, checked } : addon\n );\n }\n\n private async _updateSnapshots() {\n try {\n this._snapshots = await fetchHassioSnapshots(this.hass);\n this._snapshots.sort((a, b) => (a.date < b.date ? 1 : -1));\n } catch (err) {\n this._error = err.message;\n }\n }\n\n private async _createSnapshot() {\n this._error = \"\";\n if (this._snapshotHasPassword && !this._snapshotPassword.length) {\n this._error = \"Please enter a password.\";\n return;\n }\n this._creatingSnapshot = true;\n await this.updateComplete;\n\n const name =\n this._snapshotName ||\n new Date().toLocaleDateString(navigator.language, {\n weekday: \"long\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n\n try {\n if (this._snapshotType === \"full\") {\n const data: HassioFullSnapshotCreateParams = { name };\n if (this._snapshotHasPassword) {\n data.password = this._snapshotPassword;\n }\n await createHassioFullSnapshot(this.hass, data);\n } else {\n const addons = this._addonList\n .filter((addon) => addon.checked)\n .map((addon) => addon.slug);\n const folders = this._folderList\n .filter((folder) => folder.checked)\n .map((folder) => folder.slug);\n\n const data: HassioPartialSnapshotCreateParams = {\n name,\n folders,\n addons,\n };\n if (this._snapshotHasPassword) {\n data.password = this._snapshotPassword;\n }\n await createHassioPartialSnapshot(this.hass, data);\n }\n this._updateSnapshots();\n fireEvent(this, \"hass-api-called\", { success: true, response: null });\n } catch (err) {\n this._error = err.message;\n } finally {\n this._creatingSnapshot = false;\n }\n }\n\n private _computeDetails(snapshot: HassioSnapshot) {\n const type =\n snapshot.type === \"full\" ? \"Full snapshot\" : \"Partial snapshot\";\n return snapshot.protected ? `${type}, password protected` : type;\n }\n\n private _snapshotClicked(ev) {\n showHassioSnapshotDialog(this, {\n slug: ev.currentTarget!.snapshot.slug,\n onDelete: () => this._updateSnapshots(),\n });\n }\n\n static get styles(): CSSResultArray {\n return [\n haStyle,\n hassioStyle,\n css`\n paper-radio-group {\n display: block;\n }\n paper-radio-button {\n padding: 0 0 2px 2px;\n }\n paper-radio-button,\n paper-checkbox,\n paper-input[type=\"password\"] {\n display: block;\n margin: 4px 0 4px 48px;\n }\n .pointer {\n cursor: pointer;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-snapshots\": HassioSnapshots;\n }\n}\n","import { fireEvent } from \"../../../../src/common/dom/fire_event\";\n\nexport interface HassioSnapshotDialogParams {\n slug: string;\n onDelete: () => void;\n}\n\nexport const showHassioSnapshotDialog = (\n element: HTMLElement,\n dialogParams: HassioSnapshotDialogParams\n): void => {\n fireEvent(element, \"show-dialog\", {\n dialogTag: \"dialog-hassio-snapshot\",\n dialogImport: () =>\n import(\n /* webpackChunkName: \"dialog-hassio-snapshot\" */ \"./dialog-hassio-snapshot\"\n ),\n dialogParams,\n });\n};\n","import {\n css,\n TemplateResult,\n html,\n LitElement,\n property,\n CSSResultArray,\n} from \"lit-element\";\nimport \"@polymer/paper-card/paper-card\";\nimport memoizeOne from \"memoize-one\";\n\nimport \"../components/hassio-card-content\";\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport {\n HassioAddonInfo,\n HassioAddonRepository,\n} from \"../../../src/data/hassio/addon\";\nimport { navigate } from \"../../../src/common/navigate\";\nimport { filterAndSort } from \"../components/hassio-filter-addons\";\nimport { atLeastVersion } from \"../../../src/common/config/version\";\n\nclass HassioAddonRepositoryEl extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public repo!: HassioAddonRepository;\n @property() public addons!: HassioAddonInfo[];\n @property() public filter!: string;\n\n private _getAddons = memoizeOne(\n (addons: HassioAddonInfo[], filter?: string) => {\n if (filter) {\n return filterAndSort(addons, filter);\n }\n return addons.sort((a, b) =>\n a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1\n );\n }\n );\n\n protected render(): TemplateResult {\n const repo = this.repo;\n const addons = this._getAddons(this.addons, this.filter);\n\n if (this.filter && addons.length < 1) {\n return html`\n
\n

\n No results found in \"${repo.name}\"\n

\n
\n `;\n }\n return html`\n
\n

\n ${repo.name}\n

\n

\n Maintained by ${repo.maintainer}
\n \n ${repo.url}\n \n

\n
\n ${addons.map(\n (addon) => html`\n ${addon.advanced && !this.hass.userData?.showAdvanced\n ? \"\"\n : html`\n \n
\n \n
\n \n `}\n `\n )}\n
\n
\n `;\n }\n\n private _addonTapped(ev) {\n navigate(this, `/hassio/addon/${ev.currentTarget.addon.slug}`);\n }\n\n static get styles(): CSSResultArray {\n return [\n hassioStyle,\n css`\n paper-card {\n cursor: pointer;\n }\n .not_available {\n opacity: 0.6;\n }\n a.repo {\n color: var(--primary-text-color);\n }\n `,\n ];\n }\n}\n\ncustomElements.define(\"hassio-addon-repository\", HassioAddonRepositoryEl);\n","import { HassioAddonInfo } from \"../../../src/data/hassio/addon\";\nimport * as Fuse from \"fuse.js\";\n\nexport function filterAndSort(addons: HassioAddonInfo[], filter: string) {\n const options: Fuse.FuseOptions = {\n keys: [\"name\", \"description\", \"slug\"],\n caseSensitive: false,\n minMatchCharLength: 2,\n threshold: 0.2,\n };\n const fuse = new Fuse(addons, options);\n return fuse.search(filter);\n}\n","import {\n LitElement,\n html,\n CSSResultArray,\n css,\n property,\n TemplateResult,\n customElement,\n PropertyValues,\n} from \"lit-element\";\nimport \"@polymer/iron-icon/iron-icon\";\nimport \"@polymer/paper-card/paper-card\";\nimport \"@polymer/paper-input/paper-input\";\nimport memoizeOne from \"memoize-one\";\n\nimport \"../../../src/components/buttons/ha-call-api-button\";\nimport \"../components/hassio-card-content\";\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport { HassioAddonRepository } from \"../../../src/data/hassio/addon\";\nimport { PolymerChangedEvent } from \"../../../src/polymer-types\";\nimport { repeat } from \"lit-html/directives/repeat\";\n\n@customElement(\"hassio-repositories-editor\")\nclass HassioRepositoriesEditor extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public repos!: HassioAddonRepository[];\n @property() private _repoUrl = \"\";\n\n private _sortedRepos = memoizeOne((repos: HassioAddonRepository[]) =>\n repos\n .filter((repo) => repo.slug !== \"core\" && repo.slug !== \"local\")\n .sort((a, b) => (a.name < b.name ? -1 : 1))\n );\n\n protected render(): TemplateResult {\n const repos = this._sortedRepos(this.repos);\n return html`\n
\n

\n Repositories\n

\n

\n Configure which add-on repositories to fetch data from:\n

\n
\n ${// Use repeat so that the fade-out from call-service-api-button\n // stays with the correct repo after we add/delete one.\n repeat(\n repos,\n (repo) => repo.slug,\n (repo) => html`\n \n
\n \n
\n
\n \n Remove\n \n
\n
\n `\n )}\n\n \n
\n \n \n
\n
\n \n Add\n \n
\n
\n
\n
\n `;\n }\n\n protected updated(changedProps: PropertyValues) {\n super.updated(changedProps);\n\n if (changedProps.has(\"repos\")) {\n this._repoUrl = \"\";\n }\n }\n\n private _urlChanged(ev: PolymerChangedEvent) {\n this._repoUrl = ev.detail.value;\n }\n\n private computeRemoveRepoData(repoList, url) {\n const list = repoList\n .filter((repo) => repo.url !== url)\n .map((repo) => repo.source);\n return { addons_repositories: list };\n }\n\n private computeAddRepoData(repoList, url) {\n const list = repoList ? repoList.map((repo) => repo.source) : [];\n list.push(url);\n return { addons_repositories: list };\n }\n\n static get styles(): CSSResultArray {\n return [\n hassioStyle,\n css`\n .add {\n padding: 12px 16px;\n }\n iron-icon {\n color: var(--secondary-text-color);\n margin-right: 16px;\n display: inline-block;\n }\n paper-input {\n width: calc(100% - 49px);\n display: inline-block;\n margin-top: -4px;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-repositories-editor\": HassioRepositoriesEditor;\n }\n}\n","import \"@polymer/paper-spinner/paper-spinner-lite\";\nimport {\n LitElement,\n TemplateResult,\n html,\n css,\n customElement,\n CSSResult,\n} from \"lit-element\";\n\n@customElement(\"loading-screen\")\nclass LoadingScreen extends LitElement {\n protected render(): TemplateResult {\n return html`\n \n `;\n }\n\n static get styles(): CSSResult {\n return css`\n :host {\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"loading-screen\": LoadingScreen;\n }\n}\n","import { TemplateResult, html } from \"lit-html\";\nimport {\n css,\n CSSResult,\n customElement,\n LitElement,\n property,\n} from \"lit-element\";\nimport { fireEvent } from \"../../../src/common/dom/fire_event\";\nimport \"@polymer/iron-icon/iron-icon\";\nimport \"@polymer/paper-input/paper-input\";\nimport \"@polymer/paper-icon-button/paper-icon-button\";\nimport \"@material/mwc-button\";\n\n@customElement(\"hassio-search-input\")\nclass HassioSearchInput extends LitElement {\n @property() private filter?: string;\n\n protected render(): TemplateResult {\n return html`\n
\n \n \n ${this.filter &&\n html`\n \n `}\n \n
\n `;\n }\n\n private async _filterChanged(value: string) {\n fireEvent(this, \"value-changed\", { value: String(value) });\n }\n\n private async _filterInputChanged(e) {\n this._filterChanged(e.target.value);\n }\n\n private async _clearSearch() {\n this._filterChanged(\"\");\n }\n\n static get styles(): CSSResult {\n return css`\n paper-input {\n flex: 1 1 auto;\n margin: 0 16px;\n }\n .search-container {\n display: inline-flex;\n width: 100%;\n align-items: center;\n }\n .prefix {\n margin: 8px;\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-search-input\": HassioSearchInput;\n }\n}\n","import \"./hassio-addon-repository\";\nimport \"./hassio-repositories-editor\";\nimport { TemplateResult, html } from \"lit-html\";\nimport {\n LitElement,\n CSSResult,\n css,\n property,\n PropertyValues,\n} from \"lit-element\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport {\n HassioAddonRepository,\n HassioAddonInfo,\n fetchHassioAddonsInfo,\n reloadHassioAddons,\n} from \"../../../src/data/hassio/addon\";\nimport \"../../../src/layouts/loading-screen\";\nimport \"../components/hassio-search-input\";\n\nconst sortRepos = (a: HassioAddonRepository, b: HassioAddonRepository) => {\n if (a.slug === \"local\") {\n return -1;\n }\n if (b.slug === \"local\") {\n return 1;\n }\n if (a.slug === \"core\") {\n return -1;\n }\n if (b.slug === \"core\") {\n return 1;\n }\n return a.name.toUpperCase() < b.name.toUpperCase() ? -1 : 1;\n};\n\nclass HassioAddonStore extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() private _addons?: HassioAddonInfo[];\n @property() private _repos?: HassioAddonRepository[];\n @property() private _filter?: string;\n\n public async refreshData() {\n this._repos = undefined;\n this._addons = undefined;\n this._filter = undefined;\n await reloadHassioAddons(this.hass);\n await this._loadData();\n }\n\n protected render(): TemplateResult {\n if (!this._addons || !this._repos) {\n return html`\n \n `;\n }\n const repos: TemplateResult[] = [];\n\n for (const repo of this._repos) {\n const addons = this._addons!.filter(\n (addon) => addon.repository === repo.slug\n );\n\n if (addons.length === 0) {\n continue;\n }\n\n repos.push(html`\n \n `);\n }\n\n return html`\n \n\n \n\n ${repos}\n `;\n }\n\n protected firstUpdated(changedProps: PropertyValues) {\n super.firstUpdated(changedProps);\n this.addEventListener(\"hass-api-called\", (ev) => this.apiCalled(ev));\n this._loadData();\n }\n\n private apiCalled(ev) {\n if (ev.detail.success) {\n this._loadData();\n }\n }\n\n private async _loadData() {\n try {\n const addonsInfo = await fetchHassioAddonsInfo(this.hass);\n this._repos = addonsInfo.repositories;\n this._repos.sort(sortRepos);\n this._addons = addonsInfo.addons;\n } catch (err) {\n alert(\"Failed to fetch add-on info\");\n }\n }\n\n private async _filterChanged(e) {\n this._filter = e.detail.value;\n }\n\n static get styles(): CSSResult {\n return css`\n hassio-addon-repository {\n margin-top: 24px;\n }\n `;\n }\n}\n\ncustomElements.define(\"hassio-addon-store\", HassioAddonStore);\n","import \"@material/mwc-button\";\nimport \"@polymer/paper-card/paper-card\";\nimport {\n css,\n CSSResult,\n customElement,\n html,\n LitElement,\n property,\n TemplateResult,\n} from \"lit-element\";\n\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\nimport {\n HassioHostInfo as HassioHostInfoType,\n HassioHassOSInfo,\n} from \"../../../src/data/hassio/host\";\nimport { fetchHassioHardwareInfo } from \"../../../src/data/hassio/hardware\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport { showHassioMarkdownDialog } from \"../dialogs/markdown/show-dialog-hassio-markdown\";\n\nimport \"../../../src/components/buttons/ha-call-api-button\";\n\n@customElement(\"hassio-host-info\")\nclass HassioHostInfo extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public hostInfo!: HassioHostInfoType;\n @property() public hassOsInfo!: HassioHassOSInfo;\n @property() private _errors?: string;\n\n public render(): TemplateResult | void {\n return html`\n \n
\n

Host system

\n \n \n \n \n \n \n \n \n \n \n ${this.hostInfo.deployment\n ? html`\n \n \n \n \n `\n : \"\"}\n \n
Hostname${this.hostInfo.hostname}
System${this.hostInfo.operating_system}
Deployment${this.hostInfo.deployment}
\n \n Hardware\n \n ${this.hostInfo.features.includes(\"hostname\")\n ? html`\n \n Change hostname\n \n `\n : \"\"}\n ${this._errors\n ? html`\n
Error: ${this._errors}
\n `\n : \"\"}\n
\n
\n ${this.hostInfo.features.includes(\"reboot\")\n ? html`\n Reboot\n `\n : \"\"}\n ${this.hostInfo.features.includes(\"shutdown\")\n ? html`\n Shutdown\n `\n : \"\"}\n ${this.hostInfo.features.includes(\"hassos\")\n ? html`\n Import from USB\n `\n : \"\"}\n ${this.hostInfo.version !== this.hostInfo.version_latest\n ? html`\n Update\n `\n : \"\"}\n
\n
\n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n hassioStyle,\n css`\n paper-card {\n height: 100%;\n width: 100%;\n }\n .card-content {\n color: var(--primary-text-color);\n box-sizing: border-box;\n height: calc(100% - 47px);\n }\n .info {\n width: 100%;\n }\n .info td:nth-child(2) {\n text-align: right;\n }\n .errors {\n color: var(--google-red-500);\n margin-top: 16px;\n }\n mwc-button.info {\n max-width: calc(50% - 12px);\n }\n table.info {\n margin-bottom: 10px;\n }\n .warning {\n --mdc-theme-primary: var(--google-red-500);\n }\n `,\n ];\n }\n\n protected firstUpdated(): void {\n this.addEventListener(\"hass-api-called\", (ev) => this._apiCalled(ev));\n }\n\n private _apiCalled(ev): void {\n if (ev.detail.success) {\n this._errors = undefined;\n return;\n }\n\n const response = ev.detail.response;\n\n this._errors =\n typeof response.body === \"object\"\n ? response.body.message || \"Unknown error\"\n : response.body;\n }\n\n private async _showHardware(): Promise {\n try {\n const content = this._objectToMarkdown(\n await fetchHassioHardwareInfo(this.hass)\n );\n showHassioMarkdownDialog(this, {\n title: \"Hardware\",\n content,\n });\n } catch (err) {\n showHassioMarkdownDialog(this, {\n title: \"Hardware\",\n content: \"Error getting hardware info\",\n });\n }\n }\n\n private _objectToMarkdown(obj, indent = \"\"): string {\n let data = \"\";\n Object.keys(obj).forEach((key) => {\n if (typeof obj[key] !== \"object\") {\n data += `${indent}- ${key}: ${obj[key]}\\n`;\n } else {\n data += `${indent}- ${key}:\\n`;\n if (Array.isArray(obj[key])) {\n if (obj[key].length) {\n data +=\n `${indent} - ` + obj[key].join(`\\n${indent} - `) + \"\\n\";\n }\n } else {\n data += this._objectToMarkdown(obj[key], ` ${indent}`);\n }\n }\n });\n\n return data;\n }\n\n private _changeHostnameClicked(): void {\n const curHostname = this.hostInfo.hostname;\n const hostname = prompt(\"Please enter a new hostname:\", curHostname);\n if (hostname && hostname !== curHostname) {\n this.hass.callApi(\"POST\", \"hassio/host/options\", { hostname });\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-host-info\": HassioHostInfo;\n }\n}\n","import \"@material/mwc-button\";\nimport \"@polymer/paper-card/paper-card\";\nimport {\n css,\n CSSResult,\n customElement,\n html,\n LitElement,\n property,\n TemplateResult,\n} from \"lit-element\";\n\nimport { fireEvent } from \"../../../src/common/dom/fire_event\";\nimport {\n HassioSupervisorInfo as HassioSupervisorInfoType,\n setSupervisorOption,\n SupervisorOptions,\n} from \"../../../src/data/hassio/supervisor\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\n\nimport \"../../../src/components/buttons/ha-call-api-button\";\n\n@customElement(\"hassio-supervisor-info\")\nclass HassioSupervisorInfo extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public supervisorInfo!: HassioSupervisorInfoType;\n @property() private _errors?: string;\n\n public render(): TemplateResult | void {\n return html`\n \n
\n

Supervisor

\n \n \n \n \n \n \n \n \n \n \n ${this.supervisorInfo.channel !== \"stable\"\n ? html`\n \n \n \n \n `\n : \"\"}\n \n
Version${this.supervisorInfo.version}
Latest version${this.supervisorInfo.version_latest}
Channel${this.supervisorInfo.channel}
\n ${this._errors\n ? html`\n
Error: ${this._errors}
\n `\n : \"\"}\n
\n
\n Reload\n ${this.supervisorInfo.version !== this.supervisorInfo.version_latest\n ? html`\n Update\n `\n : \"\"}\n ${this.supervisorInfo.channel === \"beta\"\n ? html`\n Leave beta channel\n `\n : \"\"}\n ${this.supervisorInfo.channel === \"stable\"\n ? html`\n Join beta channel\n `\n : \"\"}\n
\n
\n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n hassioStyle,\n css`\n paper-card {\n height: 100%;\n width: 100%;\n }\n .card-content {\n color: var(--primary-text-color);\n box-sizing: border-box;\n height: calc(100% - 47px);\n }\n .info {\n width: 100%;\n }\n .info td:nth-child(2) {\n text-align: right;\n }\n .errors {\n color: var(--google-red-500);\n margin-top: 16px;\n }\n `,\n ];\n }\n\n protected firstUpdated(): void {\n this.addEventListener(\"hass-api-called\", (ev) => this._apiCalled(ev));\n }\n\n private _apiCalled(ev): void {\n if (ev.detail.success) {\n this._errors = undefined;\n return;\n }\n\n const response = ev.detail.response;\n\n this._errors =\n typeof response.body === \"object\"\n ? response.body.message || \"Unknown error\"\n : response.body;\n }\n\n private async _joinBeta() {\n if (\n !confirm(`WARNING:\nBeta releases are for testers and early adopters and can contain unstable code changes. Make sure you have backups of your data before you activate this feature.\n\nThis includes beta releases for:\n- Home Assistant (Release Candidates)\n- Hass.io supervisor\n- Host system`)\n ) {\n return;\n }\n try {\n const data: SupervisorOptions = { channel: \"beta\" };\n await setSupervisorOption(this.hass, data);\n const eventdata = {\n success: true,\n response: undefined,\n path: \"option\",\n };\n fireEvent(this, \"hass-api-called\", eventdata);\n } catch (err) {\n this._errors = `Error joining beta channel, ${err.body?.message || err}`;\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-supervisor-info\": HassioSupervisorInfo;\n }\n}\n","import \"@material/mwc-button\";\nimport \"@polymer/paper-card/paper-card\";\nimport {\n css,\n CSSResult,\n customElement,\n html,\n LitElement,\n property,\n TemplateResult,\n query,\n} from \"lit-element\";\n\nimport { ANSI_HTML_STYLE, parseTextToColoredPre } from \"../ansi-to-html\";\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\nimport { HomeAssistant } from \"../../../src/types\";\nimport { fetchSupervisorLogs } from \"../../../src/data/hassio/supervisor\";\n\n@customElement(\"hassio-supervisor-log\")\nclass HassioSupervisorLog extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() private _error?: string;\n @query(\"#content\") private _logContent!: HTMLDivElement;\n\n public async connectedCallback(): Promise {\n super.connectedCallback();\n await this._loadData();\n }\n\n public render(): TemplateResult | void {\n return html`\n \n ${this._error\n ? html`\n
${this._error}
\n `\n : \"\"}\n
\n
\n Refresh\n
\n
\n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n hassioStyle,\n ANSI_HTML_STYLE,\n css`\n paper-card {\n width: 100%;\n }\n pre {\n white-space: pre-wrap;\n }\n .errors {\n color: var(--google-red-500);\n margin-bottom: 16px;\n }\n `,\n ];\n }\n\n private async _loadData(): Promise {\n this._error = undefined;\n try {\n const content = await fetchSupervisorLogs(this.hass);\n while (this._logContent.lastChild) {\n this._logContent.removeChild(this._logContent.lastChild as Node);\n }\n this._logContent.appendChild(parseTextToColoredPre(content));\n } catch (err) {\n this._error = `Failed to get supervisor logs, ${err.body?.message ||\n err}`;\n }\n }\n\n private async _refresh(): Promise {\n await this._loadData();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-supervisor-log\": HassioSupervisorLog;\n }\n}\n","import \"@polymer/paper-menu-button/paper-menu-button\";\nimport {\n css,\n CSSResult,\n customElement,\n html,\n LitElement,\n property,\n TemplateResult,\n} from \"lit-element\";\n\nimport { hassioStyle } from \"../resources/hassio-style\";\nimport { haStyle } from \"../../../src/resources/styles\";\nimport {\n HassioHostInfo,\n HassioHassOSInfo,\n} from \"../../../src/data/hassio/host\";\nimport { HassioSupervisorInfo } from \"../../../src/data/hassio/supervisor\";\nimport { HomeAssistant } from \"../../../src/types\";\n\nimport \"./hassio-host-info\";\nimport \"./hassio-supervisor-info\";\nimport \"./hassio-supervisor-log\";\n\n@customElement(\"hassio-system\")\nclass HassioSystem extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public supervisorInfo!: HassioSupervisorInfo;\n @property() public hostInfo!: HassioHostInfo;\n @property() public hassOsInfo!: HassioHassOSInfo;\n\n public render(): TemplateResult | void {\n return html`\n
\n

Information

\n
\n \n \n
\n

System log

\n \n
\n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyle,\n hassioStyle,\n css`\n .content {\n margin: 8px;\n color: var(--primary-text-color);\n }\n .title {\n margin-top: 24px;\n color: var(--primary-text-color);\n font-size: 2em;\n padding-left: 8px;\n margin-bottom: 8px;\n }\n hassio-supervisor-log {\n width: 100%;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-system\": HassioSystem;\n }\n}\n","import {\n HassRouterPage,\n RouterOptions,\n} from \"../../src/layouts/hass-router-page\";\nimport { customElement, property } from \"lit-element\";\nimport { PolymerElement } from \"@polymer/polymer\";\nimport { HomeAssistant } from \"../../src/types\";\n// Don't codesplit it, that way the dashboard always loads fast.\nimport \"./dashboard/hassio-dashboard\";\n// Don't codesplit the others, because it breaks the UI when pushed to a Pi\nimport \"./snapshots/hassio-snapshots\";\nimport \"./addon-store/hassio-addon-store\";\nimport \"./system/hassio-system\";\nimport { HassioHostInfo, HassioHassOSInfo } from \"../../src/data/hassio/host\";\nimport {\n HassioSupervisorInfo,\n HassioHomeAssistantInfo,\n} from \"../../src/data/hassio/supervisor\";\n\n@customElement(\"hassio-tabs-router\")\nclass HassioTabsRouter extends HassRouterPage {\n @property() public hass!: HomeAssistant;\n @property() public supervisorInfo: HassioSupervisorInfo;\n @property() public hostInfo: HassioHostInfo;\n @property() public hassInfo: HassioHomeAssistantInfo;\n @property() public hassOsInfo!: HassioHassOSInfo;\n\n protected routerOptions: RouterOptions = {\n routes: {\n dashboard: {\n tag: \"hassio-dashboard\",\n },\n snapshots: {\n tag: \"hassio-snapshots\",\n },\n store: {\n tag: \"hassio-addon-store\",\n },\n system: {\n tag: \"hassio-system\",\n },\n },\n };\n\n protected updatePageEl(el) {\n if (\"setProperties\" in el) {\n // As long as we have Polymer pages\n (el as PolymerElement).setProperties({\n hass: this.hass,\n supervisorInfo: this.supervisorInfo,\n hostInfo: this.hostInfo,\n hassInfo: this.hassInfo,\n hassOsInfo: this.hassOsInfo,\n });\n } else {\n el.hass = this.hass;\n el.supervisorInfo = this.supervisorInfo;\n el.hostInfo = this.hostInfo;\n el.hassInfo = this.hassInfo;\n el.hassOsInfo = this.hassOsInfo;\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-tabs-router\": HassioTabsRouter;\n }\n}\n","import {\n LitElement,\n TemplateResult,\n html,\n CSSResultArray,\n css,\n customElement,\n property,\n} from \"lit-element\";\nimport \"@polymer/app-layout/app-header-layout/app-header-layout\";\nimport \"@polymer/app-layout/app-header/app-header\";\nimport \"@polymer/app-layout/app-toolbar/app-toolbar\";\nimport \"@polymer/paper-icon-button/paper-icon-button\";\nimport \"@polymer/paper-tabs/paper-tab\";\nimport \"@polymer/paper-tabs/paper-tabs\";\n\nimport \"../../src/components/ha-menu-button\";\nimport \"../../src/resources/ha-style\";\nimport \"./hassio-tabs-router\";\n\nimport scrollToTarget from \"../../src/common/dom/scroll-to-target\";\n\nimport { haStyle } from \"../../src/resources/styles\";\nimport { HomeAssistant, Route } from \"../../src/types\";\nimport { navigate } from \"../../src/common/navigate\";\nimport { HassioHostInfo, HassioHassOSInfo } from \"../../src/data/hassio/host\";\nimport {\n HassioSupervisorInfo,\n HassioHomeAssistantInfo,\n} from \"../../src/data/hassio/supervisor\";\n\nconst HAS_REFRESH_BUTTON = [\"store\", \"snapshots\"];\n\n@customElement(\"hassio-pages-with-tabs\")\nclass HassioPagesWithTabs extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public narrow!: boolean;\n @property() public route!: Route;\n @property() public supervisorInfo!: HassioSupervisorInfo;\n @property() public hostInfo!: HassioHostInfo;\n @property() public hassInfo!: HassioHomeAssistantInfo;\n @property() public hassOsInfo!: HassioHassOSInfo;\n\n protected render(): TemplateResult {\n const page = this._page;\n return html`\n \n \n \n \n
Supervisor
\n ${HAS_REFRESH_BUTTON.includes(page)\n ? html`\n \n `\n : undefined}\n
\n \n Dashboard\n Snapshots\n Add-on store\n System\n \n
\n \n
\n `;\n }\n\n private handlePageSelected(ev) {\n const newPage = ev.detail.item.getAttribute(\"page-name\");\n if (newPage !== this._page) {\n navigate(this, `/hassio/${newPage}`);\n }\n\n scrollToTarget(\n this,\n // @ts-ignore\n this.shadowRoot!.querySelector(\"app-header-layout\").header.scrollTarget\n );\n }\n\n private refreshClicked() {\n if (this._page === \"snapshots\") {\n // @ts-ignore\n this.shadowRoot.querySelector(\"hassio-snapshots\").refreshData();\n } else {\n // @ts-ignore\n this.shadowRoot.querySelector(\"hassio-addon-store\").refreshData();\n }\n }\n\n private get _page() {\n return this.route.path.substr(1);\n }\n\n static get styles(): CSSResultArray {\n return [\n haStyle,\n css`\n :host {\n color: var(--primary-text-color);\n --paper-card-header-color: var(--primary-text-color);\n }\n paper-tabs {\n margin-left: 12px;\n --paper-tabs-selection-bar-color: var(--text-primary-color, #fff);\n text-transform: uppercase;\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-pages-with-tabs\": HassioPagesWithTabs;\n }\n}\n","/**\n * Scroll to a specific y coordinate.\n *\n * Copied from paper-scroll-header-panel.\n *\n * @method scroll\n * @param {number} top The coordinate to scroll to, along the y-axis.\n * @param {boolean} smooth true if the scroll position should be smoothly adjusted.\n */\nexport default function scrollToTarget(element, target) {\n // the scroll event will trigger _updateScrollState directly,\n // However, _updateScrollState relies on the previous `scrollTop` to update the states.\n // Calling _updateScrollState will ensure that the states are synced correctly.\n const top = 0;\n const scroller = target;\n const easingFn = function easeOutQuad(t, b, c, d) {\n /* eslint-disable no-param-reassign, space-infix-ops, no-mixed-operators */\n t /= d;\n return -c * t * (t - 2) + b;\n /* eslint-enable no-param-reassign, space-infix-ops, no-mixed-operators */\n };\n const animationId = Math.random();\n const duration = 200;\n const startTime = Date.now();\n const currentScrollTop = scroller.scrollTop;\n const deltaScrollTop = top - currentScrollTop;\n element._currentAnimationId = animationId;\n (function updateFrame() {\n const now = Date.now();\n const elapsedTime = now - startTime;\n if (elapsedTime > duration) {\n scroller.scrollTop = top;\n } else if (element._currentAnimationId === animationId) {\n scroller.scrollTop = easingFn(\n elapsedTime,\n currentScrollTop,\n deltaScrollTop,\n duration\n );\n requestAnimationFrame(updateFrame.bind(element));\n }\n }.call(element));\n}\n","import { customElement, PropertyValues, property } from \"lit-element\";\nimport { PolymerElement } from \"@polymer/polymer\";\nimport \"@polymer/paper-icon-button\";\n\nimport \"../../src/resources/ha-style\";\nimport { applyThemesOnElement } from \"../../src/common/dom/apply_themes_on_element\";\nimport { fireEvent } from \"../../src/common/dom/fire_event\";\nimport {\n HassRouterPage,\n RouterOptions,\n} from \"../../src/layouts/hass-router-page\";\nimport { HomeAssistant } from \"../../src/types\";\nimport {\n fetchHassioSupervisorInfo,\n fetchHassioHomeAssistantInfo,\n HassioSupervisorInfo,\n HassioHomeAssistantInfo,\n createHassioSession,\n HassioPanelInfo,\n} from \"../../src/data/hassio/supervisor\";\nimport {\n fetchHassioHostInfo,\n fetchHassioHassOsInfo,\n HassioHostInfo,\n HassioHassOSInfo,\n} from \"../../src/data/hassio/host\";\nimport { fetchHassioAddonInfo } from \"../../src/data/hassio/addon\";\nimport { makeDialogManager } from \"../../src/dialogs/make-dialog-manager\";\nimport { ProvideHassLitMixin } from \"../../src/mixins/provide-hass-lit-mixin\";\n// Don't codesplit it, that way the dashboard always loads fast.\nimport \"./hassio-pages-with-tabs\";\nimport { navigate } from \"../../src/common/navigate\";\nimport {\n showAlertDialog,\n AlertDialogParams,\n} from \"../../src/dialogs/generic/show-dialog-box\";\n\n// The register callback of the IronA11yKeysBehavior inside paper-icon-button\n// is not called, causing _keyBindings to be uninitiliazed for paper-icon-button,\n// causing an exception when added to DOM. When transpiled to ES5, this will\n// break the build.\ncustomElements.get(\"paper-icon-button\").prototype._keyBindings = {};\n\n@customElement(\"hassio-main\")\nclass HassioMain extends ProvideHassLitMixin(HassRouterPage) {\n @property() public hass!: HomeAssistant;\n @property() public panel!: HassioPanelInfo;\n @property() public narrow!: boolean;\n\n protected routerOptions: RouterOptions = {\n // Hass.io has a page with tabs, so we route all non-matching routes to it.\n defaultPage: \"dashboard\",\n initialLoad: () => this._fetchData(),\n showLoading: true,\n routes: {\n dashboard: {\n tag: \"hassio-pages-with-tabs\",\n cache: true,\n },\n snapshots: \"dashboard\",\n store: \"dashboard\",\n system: \"dashboard\",\n addon: {\n tag: \"hassio-addon-view\",\n load: () =>\n import(\n /* webpackChunkName: \"hassio-addon-view\" */ \"./addon-view/hassio-addon-view\"\n ),\n },\n ingress: {\n tag: \"hassio-ingress-view\",\n load: () =>\n import(\n /* webpackChunkName: \"hassio-ingress-view\" */ \"./ingress-view/hassio-ingress-view\"\n ),\n },\n },\n };\n @property() private _supervisorInfo: HassioSupervisorInfo;\n @property() private _hostInfo: HassioHostInfo;\n @property() private _hassOsInfo?: HassioHassOSInfo;\n @property() private _hassInfo: HassioHomeAssistantInfo;\n\n protected firstUpdated(changedProps: PropertyValues) {\n super.firstUpdated(changedProps);\n\n applyThemesOnElement(\n this.parentElement,\n this.hass.themes,\n this.hass.selectedTheme || this.hass.themes.default_theme\n );\n this.addEventListener(\"hass-api-called\", (ev) => this._apiCalled(ev));\n // Paulus - March 17, 2019\n // We went to a single hass-toggle-menu event in HA 0.90. However, the\n // supervisor UI can also run under older versions of Home Assistant.\n // So here we are going to translate toggle events into the appropriate\n // open and close events. These events are a no-op in newer versions of\n // Home Assistant.\n this.addEventListener(\"hass-toggle-menu\", () => {\n fireEvent(\n (window.parent as any).customPanel,\n // @ts-ignore\n this.hass.dockedSidebar ? \"hass-close-menu\" : \"hass-open-menu\"\n );\n });\n // Paulus - March 19, 2019\n // We changed the navigate event to fire directly on the window, as that's\n // where we are listening for it. However, the older panel_custom will\n // listen on this element for navigation events, so we need to forward them.\n window.addEventListener(\"location-changed\", (ev) =>\n // @ts-ignore\n fireEvent(this, ev.type, ev.detail, {\n bubbles: false,\n })\n );\n\n // Forward haptic events to parent window.\n window.addEventListener(\"haptic\", (ev) => {\n // @ts-ignore\n fireEvent(window.parent, ev.type, ev.detail, {\n bubbles: false,\n });\n });\n\n makeDialogManager(this, document.body);\n }\n\n protected updatePageEl(el) {\n // the tabs page does its own routing so needs full route.\n const route =\n el.nodeName === \"HASSIO-PAGES-WITH-TABS\" ? this.route : this.routeTail;\n\n if (\"setProperties\" in el) {\n // As long as we have Polymer pages\n (el as PolymerElement).setProperties({\n hass: this.hass,\n narrow: this.narrow,\n supervisorInfo: this._supervisorInfo,\n hostInfo: this._hostInfo,\n hassInfo: this._hassInfo,\n hassOsInfo: this._hassOsInfo,\n route,\n });\n } else {\n el.hass = this.hass;\n el.narrow = this.narrow;\n el.supervisorInfo = this._supervisorInfo;\n el.hostInfo = this._hostInfo;\n el.hassInfo = this._hassInfo;\n el.hassOsInfo = this._hassOsInfo;\n el.route = route;\n }\n }\n\n private async _fetchData() {\n if (this.panel.config && this.panel.config.ingress) {\n await this._redirectIngress(this.panel.config.ingress);\n return;\n }\n\n const [supervisorInfo, hostInfo, hassInfo] = await Promise.all([\n fetchHassioSupervisorInfo(this.hass),\n fetchHassioHostInfo(this.hass),\n fetchHassioHomeAssistantInfo(this.hass),\n ]);\n this._supervisorInfo = supervisorInfo;\n this._hostInfo = hostInfo;\n this._hassInfo = hassInfo;\n\n if (this._hostInfo.features && this._hostInfo.features.includes(\"hassos\")) {\n this._hassOsInfo = await fetchHassioHassOsInfo(this.hass);\n }\n }\n\n private async _redirectIngress(addonSlug: string) {\n // When we trigger a navigation, we sleep to make sure we don't\n // show the hassio dashboard before navigating away.\n const awaitAlert = async (\n alertParams: AlertDialogParams,\n action: () => void\n ) => {\n await new Promise((resolve) => {\n alertParams.confirm = resolve;\n showAlertDialog(this, alertParams);\n });\n action();\n await new Promise((resolve) => setTimeout(resolve, 1000));\n };\n\n const createSessionPromise = createHassioSession(this.hass).then(\n () => true,\n () => false\n );\n\n let addon;\n\n try {\n addon = await fetchHassioAddonInfo(this.hass, addonSlug);\n } catch (err) {\n await awaitAlert(\n {\n text: \"Unable to fetch add-on info to start Ingress\",\n title: \"Hass.io\",\n },\n () => history.back()\n );\n\n return;\n }\n\n if (!addon.ingress_url) {\n await awaitAlert(\n {\n text: \"Add-on does not support Ingress\",\n title: addon.name,\n },\n () => history.back()\n );\n\n return;\n }\n\n if (addon.state !== \"started\") {\n await awaitAlert(\n {\n text: \"Add-on is not running. Please start it first\",\n title: addon.name,\n },\n () => navigate(this, `/hassio/addon/${addon.slug}`, true)\n );\n\n return;\n }\n\n if (!(await createSessionPromise)) {\n await awaitAlert(\n {\n text: \"Unable to create an Ingress session\",\n title: addon.name,\n },\n () => history.back()\n );\n\n return;\n }\n\n location.assign(addon.ingress_url);\n // await a promise that doesn't resolve, so we show the loading screen\n // while we load the next page.\n await new Promise(() => undefined);\n }\n\n private _apiCalled(ev) {\n if (!ev.detail.success) {\n return;\n }\n\n let tries = 1;\n\n const tryUpdate = () => {\n this._fetchData().catch(() => {\n tries += 1;\n setTimeout(tryUpdate, Math.min(tries, 5) * 1000);\n });\n };\n\n tryUpdate();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-main\": HassioMain;\n }\n}\n","import { HomeAssistant } from \"../../types\";\nimport { HassioResponse, hassioApiResultExtractor } from \"./common\";\n\nexport interface HassioAddonInfo {\n name: string;\n slug: string;\n description: string;\n repository: \"core\" | \"local\" | string;\n version: string;\n state: \"none\" | \"started\" | \"stopped\";\n installed: string | undefined;\n detached: boolean;\n available: boolean;\n build: boolean;\n advanced: boolean;\n url: string | null;\n icon: boolean;\n logo: boolean;\n}\n\nexport interface HassioAddonDetails extends HassioAddonInfo {\n name: string;\n slug: string;\n description: string;\n long_description: null | string;\n auto_update: boolean;\n url: null | string;\n detached: boolean;\n available: boolean;\n arch: \"armhf\" | \"aarch64\" | \"i386\" | \"amd64\";\n machine: any;\n homeassistant: string;\n version_latest: string;\n boot: \"auto\" | \"manual\";\n build: boolean;\n options: object;\n network: null | object;\n network_description: null | object;\n host_network: boolean;\n host_pid: boolean;\n host_ipc: boolean;\n host_dbus: boolean;\n privileged: any;\n apparmor: \"disable\" | \"default\" | \"profile\";\n devices: string[];\n auto_uart: boolean;\n icon: boolean;\n logo: boolean;\n changelog: boolean;\n hassio_api: boolean;\n hassio_role: \"default\" | \"homeassistant\" | \"manager\" | \"admin\";\n homeassistant_api: boolean;\n auth_api: boolean;\n full_access: boolean;\n protected: boolean;\n rating: \"1-6\";\n stdin: boolean;\n webui: null | string;\n gpio: boolean;\n kernel_modules: boolean;\n devicetree: boolean;\n docker_api: boolean;\n audio: boolean;\n audio_input: null | string;\n audio_output: null | string;\n services_role: string[];\n discovery: string[];\n ip_address: string;\n ingress: boolean;\n ingress_panel: boolean;\n ingress_entry: null | string;\n ingress_url: null | string;\n}\n\nexport interface HassioAddonsInfo {\n addons: HassioAddonInfo[];\n repositories: HassioAddonRepository[];\n}\n\nexport interface HassioAddonSetSecurityParams {\n protected?: boolean;\n}\n\nexport interface HassioAddonRepository {\n slug: string;\n name: string;\n source: string;\n url: string;\n maintainer: string;\n}\n\nexport interface HassioAddonSetOptionParams {\n audio_input?: string | null;\n audio_output?: string | null;\n options?: object | null;\n boot?: \"auto\" | \"manual\";\n auto_update?: boolean;\n ingress_panel?: boolean;\n network?: object | null;\n}\n\nexport const reloadHassioAddons = async (hass: HomeAssistant) => {\n await hass.callApi>(\"POST\", `hassio/addons/reload`);\n};\n\nexport const fetchHassioAddonsInfo = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\"GET\", `hassio/addons`)\n );\n};\n\nexport const fetchHassioAddonInfo = async (\n hass: HomeAssistant,\n slug: string\n) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n `hassio/addons/${slug}/info`\n )\n );\n};\n\nexport const fetchHassioAddonChangelog = async (\n hass: HomeAssistant,\n slug: string\n) => {\n return hass.callApi(\"GET\", `hassio/addons/${slug}/changelog`);\n};\n\nexport const fetchHassioAddonLogs = async (\n hass: HomeAssistant,\n slug: string\n) => {\n return hass.callApi(\"GET\", `hassio/addons/${slug}/logs`);\n};\n\nexport const setHassioAddonOption = async (\n hass: HomeAssistant,\n slug: string,\n data: HassioAddonSetOptionParams\n) => {\n await hass.callApi>(\n \"POST\",\n `hassio/addons/${slug}/options`,\n data\n );\n};\n\nexport const setHassioAddonSecurity = async (\n hass: HomeAssistant,\n slug: string,\n data: HassioAddonSetSecurityParams\n) => {\n await hass.callApi>(\n \"POST\",\n `hassio/addons/${slug}/security`,\n data\n );\n};\n\nexport const installHassioAddon = async (hass: HomeAssistant, slug: string) => {\n return hass.callApi>(\n \"POST\",\n `hassio/addons/${slug}/install`\n );\n};\n\nexport const uninstallHassioAddon = async (\n hass: HomeAssistant,\n slug: string\n) => {\n await hass.callApi>(\n \"POST\",\n `hassio/addons/${slug}/uninstall`\n );\n};\n","import { fireEvent } from \"./dom/fire_event\";\n\ndeclare global {\n // for fire event\n interface HASSDomEvents {\n \"location-changed\": {\n replace: boolean;\n };\n }\n}\n\nexport const navigate = (\n _node: any,\n path: string,\n replace: boolean = false\n) => {\n if (__DEMO__) {\n if (replace) {\n history.replaceState(null, \"\", `${location.pathname}#${path}`);\n } else {\n window.location.hash = path;\n }\n } else {\n if (replace) {\n history.replaceState(null, \"\", path);\n } else {\n history.pushState(null, \"\", path);\n }\n }\n fireEvent(window, \"location-changed\", {\n replace,\n });\n};\n","import { LocalizeFunc } from \"../translations/localize\";\n\n/**\n * Calculate a string representing a date object as relative time from now.\n *\n * Example output: 5 minutes ago, in 3 days.\n */\nconst tests = [60, 60, 24, 7];\nconst langKey = [\"second\", \"minute\", \"hour\", \"day\"];\n\nexport default function relativeTime(\n dateObj: Date,\n localize: LocalizeFunc,\n options: {\n compareTime?: Date;\n includeTense?: boolean;\n } = {}\n): string {\n const compareTime = options.compareTime || new Date();\n let delta = (compareTime.getTime() - dateObj.getTime()) / 1000;\n const tense = delta >= 0 ? \"past\" : \"future\";\n delta = Math.abs(delta);\n\n let timeDesc;\n\n for (let i = 0; i < tests.length; i++) {\n if (delta < tests[i]) {\n delta = Math.floor(delta);\n timeDesc = localize(\n `ui.components.relative_time.duration.${langKey[i]}`,\n \"count\",\n delta\n );\n break;\n }\n\n delta /= tests[i];\n }\n\n if (timeDesc === undefined) {\n delta = Math.floor(delta);\n timeDesc = localize(\n \"ui.components.relative_time.duration.week\",\n \"count\",\n delta\n );\n }\n\n return options.includeTense === false\n ? timeDesc\n : localize(`ui.components.relative_time.${tense}`, \"time\", timeDesc);\n}\n","import { dedupingMixin } from \"@polymer/polymer/lib/utils/mixin\";\n/**\n * Polymer Mixin to enable a localize function powered by language/resources from hass object.\n *\n * @polymerMixin\n */\nexport default dedupingMixin(\n (superClass) =>\n class extends superClass {\n static get properties() {\n return {\n hass: Object,\n\n /**\n * Translates a string to the current `language`. Any parameters to the\n * string should be passed in order, as follows:\n * `localize(stringKey, param1Name, param1Value, param2Name, param2Value)`\n */\n localize: {\n type: Function,\n computed: \"__computeLocalize(hass.localize)\",\n },\n };\n }\n\n __computeLocalize(localize) {\n return localize;\n }\n }\n);\n","import { dom } from \"@polymer/polymer/lib/legacy/polymer.dom\";\nimport { PolymerElement } from \"@polymer/polymer/polymer-element\";\n\nimport relativeTime from \"../common/datetime/relative_time\";\n\nimport LocalizeMixin from \"../mixins/localize-mixin\";\n\n/*\n * @appliesMixin LocalizeMixin\n */\nclass HaRelativeTime extends LocalizeMixin(PolymerElement) {\n static get properties() {\n return {\n hass: Object,\n datetime: {\n type: String,\n observer: \"datetimeChanged\",\n },\n\n datetimeObj: {\n type: Object,\n observer: \"datetimeObjChanged\",\n },\n\n parsedDateTime: Object,\n };\n }\n\n constructor() {\n super();\n this.updateRelative = this.updateRelative.bind(this);\n }\n\n connectedCallback() {\n super.connectedCallback();\n // update every 60 seconds\n this.updateInterval = setInterval(this.updateRelative, 60000);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n clearInterval(this.updateInterval);\n }\n\n datetimeChanged(newVal) {\n this.parsedDateTime = newVal ? new Date(newVal) : null;\n\n this.updateRelative();\n }\n\n datetimeObjChanged(newVal) {\n this.parsedDateTime = newVal;\n\n this.updateRelative();\n }\n\n updateRelative() {\n const root = dom(this);\n if (!this.parsedDateTime) {\n root.innerHTML = this.localize(\"ui.components.relative_time.never\");\n } else {\n root.innerHTML = relativeTime(this.parsedDateTime, this.localize);\n }\n }\n}\n\ncustomElements.define(\"ha-relative-time\", HaRelativeTime);\n","import {\n LitElement,\n TemplateResult,\n html,\n CSSResult,\n css,\n property,\n customElement,\n} from \"lit-element\";\nimport \"@polymer/iron-icon/iron-icon\";\n\nimport \"../../../src/components/ha-relative-time\";\nimport { HomeAssistant } from \"../../../src/types\";\n\n@customElement(\"hassio-card-content\")\nclass HassioCardContent extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() public title!: string;\n @property() public description?: string;\n @property({ type: Boolean }) public available: boolean = true;\n @property({ type: Boolean }) public showTopbar: boolean = false;\n @property() public topbarClass?: string;\n @property() public datetime?: string;\n @property() public iconTitle?: string;\n @property() public iconClass?: string;\n @property() public icon = \"hass:help-circle\";\n @property() public iconImage?: string;\n\n protected render(): TemplateResult {\n return html`\n ${this.showTopbar\n ? html`\n
\n `\n : \"\"}\n ${this.iconImage\n ? html`\n
\n \n
\n
\n `\n : html`\n \n `}\n
\n
\n ${this.title}\n
\n
\n ${this.description}\n ${/* treat as available when undefined */\n this.available === false ? \" (Not available)\" : \"\"}\n ${this.datetime\n ? html`\n \n `\n : undefined}\n
\n
\n `;\n }\n\n static get styles(): CSSResult {\n return css`\n iron-icon {\n margin-right: 24px;\n margin-left: 8px;\n margin-top: 12px;\n float: left;\n color: var(--secondary-text-color);\n }\n iron-icon.update {\n color: var(--paper-orange-400);\n }\n iron-icon.running,\n iron-icon.installed {\n color: var(--paper-green-400);\n }\n iron-icon.hassupdate,\n iron-icon.snapshot {\n color: var(--paper-item-icon-color);\n }\n iron-icon.not_available {\n color: var(--google-red-500);\n }\n .title {\n color: var(--primary-text-color);\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n }\n .addition {\n color: var(--secondary-text-color);\n overflow: hidden;\n position: relative;\n height: 2.4em;\n line-height: 1.2em;\n }\n ha-relative-time {\n display: block;\n }\n .icon_image img {\n max-height: 40px;\n max-width: 40px;\n margin-top: 4px;\n margin-right: 16px;\n float: left;\n }\n .icon_image.stopped,\n .icon_image.not_available {\n filter: grayscale(1);\n }\n .dot {\n position: absolute;\n background-color: var(--paper-orange-400);\n width: 12px;\n height: 12px;\n top: 8px;\n right: 8px;\n border-radius: 50%;\n }\n .topbar {\n position: absolute;\n width: 100%;\n height: 2px;\n top: 0;\n left: 0;\n border-top-left-radius: 2px;\n border-top-right-radius: 2px;\n }\n .topbar.installed {\n background-color: var(--primary-color);\n }\n .topbar.update {\n background-color: var(--accent-color);\n }\n .topbar.unavailable {\n background-color: var(--error-color);\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"hassio-card-content\": HassioCardContent;\n }\n}\n","export interface HassioResponse {\n data: T;\n result: \"ok\";\n}\n\nexport const hassioApiResultExtractor = (response: HassioResponse) =>\n response.data;\n","import { LitElement, html } from \"lit-element\";\n\nimport \"./ha-progress-button\";\nimport { fireEvent } from \"../../common/dom/fire_event\";\n\nclass HaCallApiButton extends LitElement {\n render() {\n return html`\n \n `;\n }\n\n constructor() {\n super();\n this.method = \"POST\";\n this.data = {};\n this.disabled = false;\n this.progress = false;\n }\n\n static get properties() {\n return {\n hass: {},\n progress: Boolean,\n path: String,\n method: String,\n data: {},\n disabled: Boolean,\n };\n }\n\n get progressButton() {\n return this.renderRoot.querySelector(\"ha-progress-button\");\n }\n\n async _buttonTapped() {\n this.progress = true;\n const eventData = {\n method: this.method,\n path: this.path,\n data: this.data,\n };\n\n try {\n const resp = await this.hass.callApi(this.method, this.path, this.data);\n this.progress = false;\n this.progressButton.actionSuccess();\n eventData.success = true;\n eventData.response = resp;\n } catch (err) {\n this.progress = false;\n this.progressButton.actionError();\n eventData.success = false;\n eventData.response = err;\n }\n\n fireEvent(this, \"hass-api-called\", eventData);\n }\n}\n\ncustomElements.define(\"ha-call-api-button\", HaCallApiButton);\n","import { HomeAssistant, PanelInfo } from \"../../types\";\nimport { HassioResponse, hassioApiResultExtractor } from \"./common\";\n\nexport type HassioHomeAssistantInfo = any;\nexport type HassioSupervisorInfo = any;\n\nexport type HassioPanelInfo = PanelInfo<\n | undefined\n | {\n ingress?: string;\n }\n>;\n\nexport interface CreateSessionResponse {\n session: string;\n}\n\nexport interface SupervisorOptions {\n channel: \"beta\" | \"dev\" | \"stable\";\n}\n\nexport const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n \"hassio/core/info\"\n )\n );\n};\n\nexport const fetchHassioSupervisorInfo = async (hass: HomeAssistant) => {\n return hassioApiResultExtractor(\n await hass.callApi>(\n \"GET\",\n \"hassio/supervisor/info\"\n )\n );\n};\n\nexport const fetchSupervisorLogs = async (hass: HomeAssistant) => {\n return hass.callApi(\"GET\", \"hassio/supervisor/logs\");\n};\n\nexport const createHassioSession = async (hass: HomeAssistant) => {\n const response = await hass.callApi>(\n \"POST\",\n \"hassio/ingress/session\"\n );\n document.cookie = `ingress_session=${response.data.session};path=/api/hassio_ingress/`;\n};\n\nexport const setSupervisorOption = async (\n hass: HomeAssistant,\n data: SupervisorOptions\n) => {\n await hass.callApi>(\n \"POST\",\n \"hassio/supervisor/options\",\n data\n );\n};\n","export const atLeastVersion = (\n version: string,\n major: number,\n minor: number\n): boolean => {\n const [haMajor, haMinor] = version.split(\".\", 2);\n return (\n Number(haMajor) > major ||\n (Number(haMajor) === major && Number(haMinor) >= minor)\n );\n};\n","import {\n createCollection,\n Connection,\n HassEntity,\n} from \"home-assistant-js-websocket\";\n\nexport interface PersitentNotificationEntity extends HassEntity {\n notification_id?: string;\n created_at?: string;\n title?: string;\n message?: string;\n}\n\nexport interface PersistentNotification {\n created_at: string;\n message: string;\n notification_id: string;\n title: string;\n status: \"read\" | \"unread\";\n}\n\nconst fetchNotifications = (conn) =>\n conn.sendMessagePromise({\n type: \"persistent_notification/get\",\n });\n\nconst subscribeUpdates = (conn, store) =>\n conn.subscribeEvents(\n () => fetchNotifications(conn).then((ntf) => store.setState(ntf, true)),\n \"persistent_notifications_updated\"\n );\n\nexport const subscribeNotifications = (\n conn: Connection,\n onChange: (notifications: PersistentNotification[]) => void\n) =>\n createCollection(\n \"_ntf\",\n fetchNotifications,\n subscribeUpdates,\n conn,\n onChange\n );\n","import \"@polymer/paper-icon-button/paper-icon-button\";\nimport {\n property,\n TemplateResult,\n LitElement,\n html,\n customElement,\n CSSResult,\n css,\n} from \"lit-element\";\n\nimport { fireEvent } from \"../common/dom/fire_event\";\nimport { HomeAssistant } from \"../types\";\nimport { UnsubscribeFunc } from \"home-assistant-js-websocket\";\nimport { subscribeNotifications } from \"../data/persistent_notification\";\nimport { computeDomain } from \"../common/entity/compute_domain\";\n\n@customElement(\"ha-menu-button\")\nclass HaMenuButton extends LitElement {\n @property({ type: Boolean }) public hassio = false;\n @property() public narrow!: boolean;\n @property() public hass!: HomeAssistant;\n @property() private _hasNotifications = false;\n private _alwaysVisible = false;\n private _attachNotifOnConnect = false;\n private _unsubNotifications?: UnsubscribeFunc;\n\n public connectedCallback() {\n super.connectedCallback();\n if (this._attachNotifOnConnect) {\n this._attachNotifOnConnect = false;\n this._subscribeNotifications();\n }\n }\n\n public disconnectedCallback() {\n super.disconnectedCallback();\n if (this._unsubNotifications) {\n this._attachNotifOnConnect = true;\n this._unsubNotifications();\n this._unsubNotifications = undefined;\n }\n }\n\n protected render(): TemplateResult {\n const hasNotifications =\n (this.narrow || this.hass.dockedSidebar === \"always_hidden\") &&\n (this._hasNotifications ||\n Object.keys(this.hass.states).some(\n (entityId) => computeDomain(entityId) === \"configurator\"\n ));\n return html`\n \n ${hasNotifications\n ? html`\n
\n `\n : \"\"}\n `;\n }\n\n protected firstUpdated(changedProps) {\n super.firstUpdated(changedProps);\n if (!this.hassio) {\n return;\n }\n // This component is used on Hass.io too, but Hass.io might run the UI\n // on older frontends too, that don't have an always visible menu button\n // in the sidebar.\n this._alwaysVisible =\n (Number((window.parent as any).frontendVersion) || 0) < 20190710;\n }\n\n protected updated(changedProps) {\n super.updated(changedProps);\n\n if (!changedProps.has(\"narrow\") && !changedProps.has(\"hass\")) {\n return;\n }\n\n const oldHass = changedProps.get(\"hass\") as HomeAssistant | undefined;\n const oldNarrow =\n changedProps.get(\"narrow\") ||\n (oldHass && oldHass.dockedSidebar === \"always_hidden\");\n const newNarrow =\n this.narrow || this.hass.dockedSidebar === \"always_hidden\";\n\n if (oldNarrow === newNarrow) {\n return;\n }\n\n this.style.visibility =\n newNarrow || this._alwaysVisible ? \"initial\" : \"hidden\";\n\n if (!newNarrow) {\n this._hasNotifications = false;\n if (this._unsubNotifications) {\n this._unsubNotifications();\n this._unsubNotifications = undefined;\n }\n return;\n }\n\n this._subscribeNotifications();\n }\n\n private _subscribeNotifications() {\n this._unsubNotifications = subscribeNotifications(\n this.hass.connection,\n (notifications) => {\n this._hasNotifications = notifications.length > 0;\n }\n );\n }\n\n private _toggleMenu(): void {\n fireEvent(this, \"hass-toggle-menu\");\n }\n\n static get styles(): CSSResult {\n return css`\n :host {\n position: relative;\n }\n .dot {\n pointer-events: none;\n position: absolute;\n background-color: var(--accent-color);\n width: 12px;\n height: 12px;\n top: 5px;\n right: 2px;\n border-radius: 50%;\n border: 2px solid var(--app-header-background-color);\n }\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ha-menu-button\": HaMenuButton;\n }\n}\n","export const computeDomain = (entityId: string): string => {\n return entityId.substr(0, entityId.indexOf(\".\"));\n};\n"],"sourceRoot":""} \ No newline at end of file diff --git a/supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js b/supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js new file mode 100644 index 000000000..03c533328 --- /dev/null +++ b/supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js @@ -0,0 +1,3 @@ +/*! For license information please see chunk.d9ebbcff54094bd6592a.js.LICENSE */ +(self.webpackJsonp=self.webpackJsonp||[]).push([[3],{169:function(e,t,r){"use strict";r.r(t);var n=r(5),i=(r(53),r(40),r(55),r(94),r(10)),o=r(47);function a(e){return(a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function s(){var e=h(["\n :host([inert]) {\n pointer-events: initial !important;\n cursor: initial !important;\n }\n ha-paper-dialog {\n min-width: 400px;\n max-width: 500px;\n }\n @media (max-width: 400px) {\n ha-paper-dialog {\n min-width: initial;\n }\n }\n a {\n color: var(--primary-color);\n }\n p {\n margin: 0;\n padding-top: 6px;\n padding-bottom: 24px;\n color: var(--primary-text-color);\n }\n .no-bottom-padding {\n padding-bottom: 0;\n }\n .secondary {\n color: var(--secondary-text-color);\n }\n "]);return s=function(){return e},e}function c(){var e=h(['\n \n ',"\n \n "]);return c=function(){return e},e}function l(){var e=h(["\n \n "]);return l=function(){return e},e}function d(){var e=h(["\n \n ","\n

\n "]);return d=function(){return e},e}function p(){var e=h(['\n \n

\n ',"\n

\n \n ","\n ",'\n \n
\n ','\n \n ',"\n \n
\n \n "]);return p=function(){return e},e}function u(){var e=h([""]);return u=function(){return e},e}function h(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function f(e,t,r,n,i,o,a){try{var s=e[o](a),c=s.value}catch(l){return void r(l)}s.done?t(c):Promise.resolve(c).then(n,i)}function m(e){return function(){var t=this,r=arguments;return new Promise(function(n,i){var o=e.apply(t,r);function a(e){f(o,n,i,a,s,"next",e)}function s(e){f(o,n,i,a,s,"throw",e)}a(void 0)})}}function y(e){return(y=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function v(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function b(e,t){return(b=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function g(e){var t,r=O(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function w(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function k(e){return e.decorators&&e.decorators.length}function _(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function E(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function O(e){var t=function(e,t){if("object"!==a(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==a(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===a(t)?t:String(t)}!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!k(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var d=0;d=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var a=0;a\n\n :host {\n display: block;\n @apply --layout-relative;\n }\n\n :host(.is-scrolled:not(:first-child))::before {\n content: \'\';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--divider-color);\n }\n\n :host(.can-scroll:not(.scrolled-to-bottom):not(:last-child))::after {\n content: \'\';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--divider-color);\n }\n\n .scrollable {\n padding: 0 24px;\n\n @apply --layout-scroll;\n @apply --paper-dialog-scrollable;\n }\n\n .fit {\n @apply --layout-fit;\n }\n \n\n
\n \n
\n']);return a=function(){return e},e}Object(i.a)({_template:Object(o.a)(a()),is:"paper-dialog-scrollable",properties:{dialogElement:{type:Object}},get scrollTarget(){return this.$.scrollable},ready:function(){this._ensureTarget(),this.classList.add("no-padding")},attached:function(){this._ensureTarget(),requestAnimationFrame(this.updateScrollState.bind(this))},updateScrollState:function(){this.toggleClass("is-scrolled",this.scrollTarget.scrollTop>0),this.toggleClass("can-scroll",this.scrollTarget.offsetHeight=this.scrollTarget.scrollHeight)},_ensureTarget:function(){this.dialogElement=this.dialogElement||this.parentElement,this.dialogElement&&this.dialogElement.behaviors&&this.dialogElement.behaviors.indexOf(n.b)>=0?(this.dialogElement.sizingTarget=this.scrollTarget,this.scrollTarget.classList.remove("fit")):this.dialogElement&&this.scrollTarget.classList.add("fit")}})},55:function(e,t,r){"use strict";r(4),r(12),r(13),r(30),r(35);var n=document.createElement("template");n.setAttribute("style","display: none;"),n.innerHTML='\n \n',document.head.appendChild(n.content);var i=r(72),o=r(31),a=r(7),s=r(6);function c(){var e=function(e,t){t||(t=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}(['\n \n \n']);return c=function(){return e},e}Object(a.a)({_template:Object(s.a)(c()),is:"paper-dialog",behaviors:[o.a,i.a],listeners:{"neon-animation-finish":"_onNeonAnimationFinish"},_renderOpened:function(){this.cancelAnimation(),this.playAnimation("entry")},_renderClosed:function(){this.cancelAnimation(),this.playAnimation("exit")},_onNeonAnimationFinish:function(){this.opened?this._finishRenderOpened():this._finishRenderClosed()}});var l=r(59),d=r(8),p=r(67),u={getTabbableNodes:function(e){var t=[];return this._collectTabbableNodes(e,t)?p.a._sortByTabIndex(t):t},_collectTabbableNodes:function(e,t){if(e.nodeType!==Node.ELEMENT_NODE||!p.a._isVisible(e))return!1;var r,n=e,i=p.a._normalizedTabIndex(n),o=i>0;i>=0&&t.push(n),r="content"===n.localName||"slot"===n.localName?Object(d.a)(n).getDistributedNodes():Object(d.a)(n.shadowRoot||n.root||n).children;for(var a=0;a\n
\n \n
\n \n
\n
\n
\n \n ']);return l=function(){return e},e}function d(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function p(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function u(e,t){return(u=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function h(e){var t,r=b(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var n={kind:"field"===e.kind?"field":"method",key:r,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(n.decorators=e.decorators),"field"===e.kind&&(n.initializer=e.value),n}function f(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function m(e){return e.decorators&&e.decorators.length}function y(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function v(e,t){var r=e[t];if(void 0!==r&&"function"!=typeof r)throw new TypeError("Expected '"+t+"' to be a function");return r}function b(e){var t=function(e,t){if("object"!==s(e)||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!==s(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===s(t)?t:String(t)}function g(e,t,r){return(g="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(e,t,r){var n=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=w(e)););return e}(e,t);if(n){var i=Object.getOwnPropertyDescriptor(n,t);return i.get?i.get.call(r):i.value}})(e,t,r||e)}function w(e){return(w=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var k=customElements.get("mwc-switch");!function(e,t,r,n){var i=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(r){t.forEach(function(t){t.kind===r&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var r=e.prototype;["method","field"].forEach(function(n){t.forEach(function(t){var i=t.placement;if(t.kind===n&&("static"===i||"prototype"===i)){var o="static"===i?e:r;this.defineClassElement(o,t)}},this)},this)},defineClassElement:function(e,t){var r=t.descriptor;if("field"===t.kind){var n=t.initializer;r={enumerable:r.enumerable,writable:r.writable,configurable:r.configurable,value:void 0===n?void 0:n.call(e)}}Object.defineProperty(e,t.key,r)},decorateClass:function(e,t){var r=[],n=[],i={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,i)},this),e.forEach(function(e){if(!m(e))return r.push(e);var t=this.decorateElement(e,i);r.push(t.element),r.push.apply(r,t.extras),n.push.apply(n,t.finishers)},this),!t)return{elements:r,finishers:n};var o=this.decorateConstructor(r,t);return n.push.apply(n,o.finishers),o.finishers=n,o},addElementPlacement:function(e,t,r){var n=t[e.placement];if(!r&&-1!==n.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");n.push(e.key)},decorateElement:function(e,t){for(var r=[],n=[],i=e.decorators,o=i.length-1;o>=0;o--){var a=t[e.placement];a.splice(a.indexOf(e.key),1);var s=this.fromElementDescriptor(e),c=this.toElementFinisherExtras((0,i[o])(s)||s);e=c.element,this.addElementPlacement(e,t),c.finisher&&n.push(c.finisher);var l=c.extras;if(l){for(var d=0;d=0;n--){var i=this.fromClassDescriptor(e),o=this.toClassDescriptor((0,t[n])(i)||i);if(void 0!==o.finisher&&r.push(o.finisher),void 0!==o.elements){e=o.elements;for(var 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 literal 0 HcmV?d00001 diff --git a/supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js.map b/supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js.map new file mode 100644 index 000000000..0758d1c28 --- /dev/null +++ b/supervisor/api/panel/chunk.d9ebbcff54094bd6592a.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///./src/dialogs/generic/dialog-box.ts","webpack:///./node_modules/@polymer/paper-dialog-behavior/paper-dialog-behavior.js","webpack:///./node_modules/@polymer/paper-dialog-scrollable/paper-dialog-scrollable.js","webpack:///./node_modules/@polymer/paper-dialog-behavior/paper-dialog-shared-styles.js","webpack:///./node_modules/@polymer/paper-dialog/paper-dialog.js","webpack:///./src/components/dialog/ha-iron-focusables-helper.js","webpack:///./src/components/dialog/ha-paper-dialog.ts","webpack:///./src/components/ha-switch.ts","webpack:///./src/data/haptics.ts"],"names":["customElement","property","_callee","params","regeneratorRuntime","wrap","_context","prev","next","this","_params","prompt","_value","defaultValue","stop","html","_templateObject","confirmPrompt","confirmation","_templateObject2","_openedChanged","title","hass","localize","text","_templateObject3","classMap","no-bottom-padding","Boolean","_templateObject4","_valueChanged","inputLabel","inputType","_templateObject5","_dismiss","dismissText","_confirm","confirmText","ev","detail","value","_callee2","_context2","cancel","undefined","_callee3","_context3","confirm","haStyleDialog","css","_templateObject6","LitElement","__webpack_require__","d","__webpack_exports__","PaperDialogBehaviorImpl","PaperDialogBehavior","_polymer_iron_overlay_behavior_iron_overlay_behavior_js__WEBPACK_IMPORTED_MODULE_1__","_polymer_polymer_lib_legacy_polymer_dom_js__WEBPACK_IMPORTED_MODULE_2__","hostAttributes","role","tabindex","properties","modal","type","__readied","observers","listeners","tap","ready","__prevNoCancelOnOutsideClick","noCancelOnOutsideClick","__prevNoCancelOnEscKey","noCancelOnEscKey","__prevWithBackdrop","withBackdrop","_modalChanged","readied","_updateClosingReasonConfirmed","confirmed","closingReason","_onDialogClick","event","path","dom","i","l","indexOf","target","hasAttribute","close","stopPropagation","IronOverlayBehavior","Polymer","_template","is","dialogElement","Object","scrollTarget","$","scrollable","_ensureTarget","classList","add","attached","requestAnimationFrame","updateScrollState","bind","toggleClass","scrollTop","offsetHeight","scrollHeight","parentElement","behaviors","sizingTarget","remove","$_documentContainer","document","createElement","setAttribute","innerHTML","head","appendChild","content","NeonAnimationRunnerBehavior","neon-animation-finish","_renderOpened","cancelAnimation","playAnimation","_renderClosed","_onNeonAnimationFinish","opened","_finishRenderOpened","_finishRenderClosed","HaIronFocusablesHelper","getTabbableNodes","node","result","_collectTabbableNodes","IronFocusablesHelper","_sortByTabIndex","nodeType","Node","ELEMENT_NODE","_isVisible","children","element","tabIndex","_normalizedTabIndex","needsSort","push","localName","getDistributedNodes","shadowRoot","root","length","paperDialogClass","customElements","get","haTabFixBehaviorImpl","_focusableNodes","HaPaperDialog","_mixinBehaviors","_classCallCheck","_possibleConstructorReturn","_getPrototypeOf","apply","arguments","_inherits","mixinBehaviors","define","MwcSwitch","_decorate","_initialize","_MwcSwitch","HaSwitch","_MwcSwitch2","_getPrototypeOf2","_this","_len","args","Array","_key","call","concat","_assertThisInitialized","F","kind","decorators","key","query","_this2","_get","prototype","style","setProperty","toggle","_slot","assignedNodes","addEventListener","hapticType","haptic","fireEvent","window","ripple","interactionNode","_haChangeHandler","static","e","mdcFoundation","handleChange","checked","formElement"],"mappings":"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","file":"chunk.d9ebbcff54094bd6592a.js","sourcesContent":["import {\n LitElement,\n html,\n css,\n CSSResult,\n TemplateResult,\n customElement,\n property,\n} from \"lit-element\";\nimport \"@polymer/paper-dialog-scrollable/paper-dialog-scrollable\";\nimport \"@polymer/paper-input/paper-input\";\n\nimport \"../../components/dialog/ha-paper-dialog\";\nimport \"../../components/ha-switch\";\n\nimport { HomeAssistant } from \"../../types\";\nimport { DialogParams } from \"./show-dialog-box\";\nimport { PolymerChangedEvent } from \"../../polymer-types\";\nimport { haStyleDialog } from \"../../resources/styles\";\nimport { classMap } from \"lit-html/directives/class-map\";\n\n@customElement(\"dialog-box\")\nclass DialogBox extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() private _params?: DialogParams;\n @property() private _value?: string;\n\n public async showDialog(params: DialogParams): Promise {\n this._params = params;\n if (params.prompt) {\n this._value = params.defaultValue;\n }\n }\n\n protected render(): TemplateResult {\n if (!this._params) {\n return html``;\n }\n\n const confirmPrompt = this._params.confirmation || this._params.prompt;\n\n return html`\n \n

\n ${this._params.title\n ? this._params.title\n : this._params.confirmation &&\n this.hass.localize(\n \"ui.dialogs.generic.default_confirmation_title\"\n )}\n

\n \n ${this._params.text\n ? html`\n \n ${this._params.text}\n

\n `\n : \"\"}\n ${this._params.prompt\n ? html`\n \n `\n : \"\"}\n
\n
\n ${confirmPrompt &&\n html`\n \n ${this._params.dismissText\n ? this._params.dismissText\n : this.hass.localize(\"ui.dialogs.generic.cancel\")}\n \n `}\n \n ${this._params.confirmText\n ? this._params.confirmText\n : this.hass.localize(\"ui.dialogs.generic.ok\")}\n \n
\n \n `;\n }\n\n private _valueChanged(ev: PolymerChangedEvent) {\n this._value = ev.detail.value;\n }\n\n private async _dismiss(): Promise {\n if (this._params!.cancel) {\n this._params!.cancel();\n }\n this._params = undefined;\n }\n\n private async _confirm(): Promise {\n if (this._params!.confirm) {\n this._params!.confirm(this._value);\n }\n this._dismiss();\n }\n\n private _openedChanged(ev: PolymerChangedEvent): void {\n if (!(ev.detail as any).value) {\n this._params = undefined;\n }\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyleDialog,\n css`\n :host([inert]) {\n pointer-events: initial !important;\n cursor: initial !important;\n }\n ha-paper-dialog {\n min-width: 400px;\n max-width: 500px;\n }\n @media (max-width: 400px) {\n ha-paper-dialog {\n min-width: initial;\n }\n }\n a {\n color: var(--primary-color);\n }\n p {\n margin: 0;\n padding-top: 6px;\n padding-bottom: 24px;\n color: var(--primary-text-color);\n }\n .no-bottom-padding {\n padding-bottom: 0;\n }\n .secondary {\n color: var(--secondary-text-color);\n }\n `,\n ];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"dialog-box\": DialogBox;\n }\n}\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronOverlayBehavior} from '@polymer/iron-overlay-behavior/iron-overlay-behavior.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\n\n/**\n Use `Polymer.PaperDialogBehavior` and `paper-dialog-shared-styles.html` to\n implement a Material Design dialog.\n\n For example, if `` implements this behavior:\n\n \n

Header

\n
Dialog body
\n
\n Cancel\n Accept\n
\n
\n\n `paper-dialog-shared-styles.html` provide styles for a header, content area,\n and an action area for buttons. Use the `

` tag for the header and the\n `buttons` class for the action area. You can use the `paper-dialog-scrollable`\n element (in its own repository) if you need a scrolling content area.\n\n Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive\n controls to close the dialog. If the user dismisses the dialog with\n `dialog-confirm`, the `closingReason` will update to include `confirmed:\n true`.\n\n ### Accessibility\n\n This element has `role=\"dialog\"` by default. Depending on the context, it may\n be more appropriate to override this attribute with `role=\"alertdialog\"`.\n\n If `modal` is set, the element will prevent the focus from exiting the\n element. It will also ensure that focus remains in the dialog.\n\n @hero hero.svg\n @demo demo/index.html\n @polymerBehavior PaperDialogBehavior\n */\nexport const PaperDialogBehaviorImpl = {\n\n hostAttributes: {'role': 'dialog', 'tabindex': '-1'},\n\n properties: {\n\n /**\n * If `modal` is true, this implies `no-cancel-on-outside-click`,\n * `no-cancel-on-esc-key` and `with-backdrop`.\n */\n modal: {type: Boolean, value: false},\n\n __readied: {type: Boolean, value: false}\n\n },\n\n observers: ['_modalChanged(modal, __readied)'],\n\n listeners: {'tap': '_onDialogClick'},\n\n /**\n * @return {void}\n */\n ready: function() {\n // Only now these properties can be read.\n this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;\n this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;\n this.__prevWithBackdrop = this.withBackdrop;\n this.__readied = true;\n },\n\n _modalChanged: function(modal, readied) {\n // modal implies noCancelOnOutsideClick, noCancelOnEscKey and withBackdrop.\n // We need to wait for the element to be ready before we can read the\n // properties values.\n if (!readied) {\n return;\n }\n\n if (modal) {\n this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;\n this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;\n this.__prevWithBackdrop = this.withBackdrop;\n this.noCancelOnOutsideClick = true;\n this.noCancelOnEscKey = true;\n this.withBackdrop = true;\n } else {\n // If the value was changed to false, let it false.\n this.noCancelOnOutsideClick =\n this.noCancelOnOutsideClick && this.__prevNoCancelOnOutsideClick;\n this.noCancelOnEscKey =\n this.noCancelOnEscKey && this.__prevNoCancelOnEscKey;\n this.withBackdrop = this.withBackdrop && this.__prevWithBackdrop;\n }\n },\n\n _updateClosingReasonConfirmed: function(confirmed) {\n this.closingReason = this.closingReason || {};\n this.closingReason.confirmed = confirmed;\n },\n\n /**\n * Will dismiss the dialog if user clicked on an element with dialog-dismiss\n * or dialog-confirm attribute.\n */\n _onDialogClick: function(event) {\n // Search for the element with dialog-confirm or dialog-dismiss,\n // from the root target until this (excluded).\n var path = dom(event).path;\n for (var i = 0, l = path.indexOf(this); i < l; i++) {\n var target = path[i];\n if (target.hasAttribute &&\n (target.hasAttribute('dialog-dismiss') ||\n target.hasAttribute('dialog-confirm'))) {\n this._updateClosingReasonConfirmed(\n target.hasAttribute('dialog-confirm'));\n this.close();\n event.stopPropagation();\n break;\n }\n }\n }\n\n};\n\n/** @polymerBehavior */\nexport const PaperDialogBehavior =\n [IronOverlayBehavior, PaperDialogBehaviorImpl];\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\nimport '@polymer/paper-styles/default-theme.js';\n\nimport {PaperDialogBehaviorImpl} from '@polymer/paper-dialog-behavior/paper-dialog-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/**\nMaterial design:\n[Dialogs](https://www.google.com/design/spec/components/dialogs.html)\n\n`paper-dialog-scrollable` implements a scrolling area used in a Material Design\ndialog. It shows a divider at the top and/or bottom indicating more content,\ndepending on scroll position. Use this together with elements implementing\n`Polymer.PaperDialogBehavior`.\n\n \n

Header

\n \n Lorem ipsum...\n \n
\n OK\n
\n
\n\nIt shows a top divider after scrolling if it is not the first child in its\nparent container, indicating there is more content above. It shows a bottom\ndivider if it is scrollable and it is not the last child in its parent\ncontainer, indicating there is more content below. The bottom divider is hidden\nif it is scrolled to the bottom.\n\nIf `paper-dialog-scrollable` is not a direct child of the element implementing\n`Polymer.PaperDialogBehavior`, remember to set the `dialogElement`:\n\n \n

Header

\n
\n

Sub-header

\n \n Lorem ipsum...\n \n
\n
\n OK\n
\n
\n\n \n\n### Styling\nThe following custom properties and mixins are available for styling:\n\nCustom property | Description | Default\n----------------|-------------|----------\n`--paper-dialog-scrollable` | Mixin for the scrollable content | {}\n\n@group Paper Elements\n@element paper-dialog-scrollable\n@demo demo/index.html\n@hero hero.svg\n*/\nPolymer({\n _template: html`\n \n\n
\n \n
\n`,\n\n is: 'paper-dialog-scrollable',\n\n properties: {\n\n /**\n * The dialog element that implements `Polymer.PaperDialogBehavior`\n * containing this element.\n * @type {?Node}\n */\n dialogElement: {type: Object}\n\n },\n\n /**\n * Returns the scrolling element.\n */\n get scrollTarget() {\n return this.$.scrollable;\n },\n\n ready: function() {\n this._ensureTarget();\n this.classList.add('no-padding');\n },\n\n attached: function() {\n this._ensureTarget();\n requestAnimationFrame(this.updateScrollState.bind(this));\n },\n\n updateScrollState: function() {\n this.toggleClass('is-scrolled', this.scrollTarget.scrollTop > 0);\n this.toggleClass(\n 'can-scroll',\n this.scrollTarget.offsetHeight < this.scrollTarget.scrollHeight);\n this.toggleClass(\n 'scrolled-to-bottom',\n this.scrollTarget.scrollTop + this.scrollTarget.offsetHeight >=\n this.scrollTarget.scrollHeight);\n },\n\n _ensureTarget: function() {\n // Read parentElement instead of parentNode in order to skip shadowRoots.\n this.dialogElement = this.dialogElement || this.parentElement;\n // Check if dialog implements paper-dialog-behavior. If not, fit\n // scrollTarget to host.\n if (this.dialogElement && this.dialogElement.behaviors &&\n this.dialogElement.behaviors.indexOf(PaperDialogBehaviorImpl) >= 0) {\n this.dialogElement.sizingTarget = this.scrollTarget;\n this.scrollTarget.classList.remove('fit');\n } else if (this.dialogElement) {\n this.scrollTarget.classList.add('fit');\n }\n }\n});\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\n/*\n### Styling\n\nThe following custom properties and mixins are available for styling.\n\nCustom property | Description | Default\n----------------|-------------|----------\n`--paper-dialog-background-color` | Dialog background color | `--primary-background-color`\n`--paper-dialog-color` | Dialog foreground color | `--primary-text-color`\n`--paper-dialog` | Mixin applied to the dialog | `{}`\n`--paper-dialog-title` | Mixin applied to the title (`

`) element | `{}`\n`--paper-dialog-button-color` | Button area foreground color | `--default-primary-color`\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\nimport '@polymer/paper-styles/default-theme.js';\nimport '@polymer/paper-styles/typography.js';\nimport '@polymer/paper-styles/shadow.js';\nconst $_documentContainer = document.createElement('template');\n$_documentContainer.setAttribute('style', 'display: none;');\n\n$_documentContainer.innerHTML = `\n \n`;\n\ndocument.head.appendChild($_documentContainer.content);\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/paper-dialog-behavior/paper-dialog-shared-styles.js';\n\nimport {NeonAnimationRunnerBehavior} from '@polymer/neon-animation/neon-animation-runner-behavior.js';\nimport {PaperDialogBehavior} from '@polymer/paper-dialog-behavior/paper-dialog-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/**\nMaterial design:\n[Dialogs](https://www.google.com/design/spec/components/dialogs.html)\n\n`` is a dialog with Material Design styling and optional\nanimations when it is opened or closed. It provides styles for a header, content\narea, and an action area for buttons. You can use the\n`` element (in its own repository) if you need a\nscrolling content area. To autofocus a specific child element after opening the\ndialog, give it the `autofocus` attribute. See `Polymer.PaperDialogBehavior` and\n`Polymer.IronOverlayBehavior` for specifics.\n\nFor example, the following code implements a dialog with a header, scrolling\ncontent area and buttons. Focus will be given to the `dialog-confirm` button\nwhen the dialog is opened.\n\n \n

Header

\n \n Lorem ipsum...\n \n
\n Cancel\n Accept\n
\n
\n\n### Styling\n\nSee the docs for `Polymer.PaperDialogBehavior` for the custom properties\navailable for styling this element.\n\n### Animations\n\nSet the `entry-animation` and/or `exit-animation` attributes to add an animation\nwhen the dialog is opened or closed. See the documentation in\n[PolymerElements/neon-animation](https://github.com/PolymerElements/neon-animation)\nfor more info.\n\nFor example:\n\n \n\n \n

Header

\n
Dialog body
\n
\n\n### Accessibility\n\nSee the docs for `Polymer.PaperDialogBehavior` for accessibility features\nimplemented by this element.\n\n@group Paper Elements\n@element paper-dialog\n@hero hero.svg\n@demo demo/index.html\n*/\nPolymer({\n _template: html`\n \n \n`,\n\n is: 'paper-dialog',\n behaviors: [PaperDialogBehavior, NeonAnimationRunnerBehavior],\n listeners: {'neon-animation-finish': '_onNeonAnimationFinish'},\n\n _renderOpened: function() {\n this.cancelAnimation();\n this.playAnimation('entry');\n },\n\n _renderClosed: function() {\n this.cancelAnimation();\n this.playAnimation('exit');\n },\n\n _onNeonAnimationFinish: function() {\n if (this.opened) {\n this._finishRenderOpened();\n } else {\n this._finishRenderClosed();\n }\n }\n});\n","/**\n@license\nCopyright (c) 2016 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\n/*\n Fixes issue with not using shadow dom properly in iron-overlay-behavior/icon-focusables-helper.js\n*/\nimport { dom } from \"@polymer/polymer/lib/legacy/polymer.dom.js\";\n\nimport { IronFocusablesHelper } from \"@polymer/iron-overlay-behavior/iron-focusables-helper.js\";\n\nexport const HaIronFocusablesHelper = {\n /**\n * Returns a sorted array of tabbable nodes, including the root node.\n * It searches the tabbable nodes in the light and shadow dom of the chidren,\n * sorting the result by tabindex.\n * @param {!Node} node\n * @return {!Array}\n */\n getTabbableNodes: function(node) {\n var result = [];\n // If there is at least one element with tabindex > 0, we need to sort\n // the final array by tabindex.\n var needsSortByTabIndex = this._collectTabbableNodes(node, result);\n if (needsSortByTabIndex) {\n return IronFocusablesHelper._sortByTabIndex(result);\n }\n return result;\n },\n\n /**\n * Searches for nodes that are tabbable and adds them to the `result` array.\n * Returns if the `result` array needs to be sorted by tabindex.\n * @param {!Node} node The starting point for the search; added to `result`\n * if tabbable.\n * @param {!Array} result\n * @return {boolean}\n * @private\n */\n _collectTabbableNodes: function(node, result) {\n // If not an element or not visible, no need to explore children.\n if (\n node.nodeType !== Node.ELEMENT_NODE ||\n !IronFocusablesHelper._isVisible(node)\n ) {\n return false;\n }\n var element = /** @type {!HTMLElement} */ (node);\n var tabIndex = IronFocusablesHelper._normalizedTabIndex(element);\n var needsSort = tabIndex > 0;\n if (tabIndex >= 0) {\n result.push(element);\n }\n\n // In ShadowDOM v1, tab order is affected by the order of distrubution.\n // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B];\n // in ShadowDOM v0 tab order is not affected by the distrubution order,\n // in fact getTabbableNodes(#root) returns [#B, #A].\n //
\n // \n // \n // \n // \n // \n // \n //
\n // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0.\n var children;\n if (element.localName === \"content\" || element.localName === \"slot\") {\n children = dom(element).getDistributedNodes();\n } else {\n // /////////////////////////\n // Use shadow root if possible, will check for distributed nodes.\n // THIS IS THE CHANGED LINE\n children = dom(element.shadowRoot || element.root || element).children;\n // /////////////////////////\n }\n for (var i = 0; i < children.length; i++) {\n // Ensure method is always invoked to collect tabbable children.\n needsSort = this._collectTabbableNodes(children[i], result) || needsSort;\n }\n return needsSort;\n },\n};\n","import \"@polymer/paper-dialog/paper-dialog\";\nimport { mixinBehaviors } from \"@polymer/polymer/lib/legacy/class\";\nimport { HaIronFocusablesHelper } from \"./ha-iron-focusables-helper.js\";\n// tslint:disable-next-line\nimport { PaperDialogElement } from \"@polymer/paper-dialog/paper-dialog\";\n\nconst paperDialogClass = customElements.get(\"paper-dialog\");\n\n// behavior that will override existing iron-overlay-behavior and call the fixed implementation\nconst haTabFixBehaviorImpl = {\n get _focusableNodes() {\n return HaIronFocusablesHelper.getTabbableNodes(this);\n },\n};\n\n// paper-dialog that uses the haTabFixBehaviorImpl behvaior\n// export class HaPaperDialog extends paperDialogClass {}\n// @ts-ignore\nexport class HaPaperDialog\n extends mixinBehaviors([haTabFixBehaviorImpl], paperDialogClass)\n implements PaperDialogElement {}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ha-paper-dialog\": HaPaperDialog;\n }\n}\ncustomElements.define(\"ha-paper-dialog\", HaPaperDialog);\n","import {\n customElement,\n CSSResult,\n css,\n query,\n html,\n property,\n} from \"lit-element\";\nimport \"@material/mwc-switch\";\nimport { style } from \"@material/mwc-switch/mwc-switch-css\";\n// tslint:disable-next-line\nimport { Switch } from \"@material/mwc-switch\";\nimport { Constructor } from \"../types\";\nimport { forwardHaptic } from \"../data/haptics\";\nimport { ripple } from \"@material/mwc-ripple/ripple-directive\";\n// tslint:disable-next-line\nconst MwcSwitch = customElements.get(\"mwc-switch\") as Constructor;\n\n@customElement(\"ha-switch\")\nexport class HaSwitch extends MwcSwitch {\n // Generate a haptic vibration.\n // Only set to true if the new value of the switch is applied right away when toggling.\n // Do not add haptic when a user is required to press save.\n @property({ type: Boolean }) public haptic = false;\n @query(\"slot\") private _slot!: HTMLSlotElement;\n\n protected firstUpdated() {\n super.firstUpdated();\n this.style.setProperty(\n \"--mdc-theme-secondary\",\n \"var(--switch-checked-color)\"\n );\n this.classList.toggle(\n \"slotted\",\n Boolean(this._slot.assignedNodes().length)\n );\n this.addEventListener(\"change\", () => {\n if (this.haptic) {\n forwardHaptic(\"light\");\n }\n });\n }\n\n protected render() {\n return html`\n
\n
\n \n
\n \n
\n
\n \n \n `;\n }\n\n protected static get styles(): CSSResult[] {\n return [\n style,\n css`\n :host {\n display: flex;\n flex-direction: row;\n align-items: center;\n }\n .mdc-switch.mdc-switch--checked .mdc-switch__thumb {\n background-color: var(--switch-checked-button-color);\n border-color: var(--switch-checked-button-color);\n }\n .mdc-switch.mdc-switch--checked .mdc-switch__track {\n background-color: var(--switch-checked-track-color);\n border-color: var(--switch-checked-track-color);\n }\n .mdc-switch:not(.mdc-switch--checked) .mdc-switch__thumb {\n background-color: var(--switch-unchecked-button-color);\n border-color: var(--switch-unchecked-button-color);\n }\n .mdc-switch:not(.mdc-switch--checked) .mdc-switch__track {\n background-color: var(--switch-unchecked-track-color);\n border-color: var(--switch-unchecked-track-color);\n }\n :host(.slotted) .mdc-switch {\n margin-right: 24px;\n }\n `,\n ];\n }\n\n private _haChangeHandler(e: Event) {\n this.mdcFoundation.handleChange(e);\n // catch \"click\" event and sync properties\n this.checked = this.formElement.checked;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ha-switch\": HaSwitch;\n }\n}\n","/**\n * Broadcast haptic feedback requests\n */\n\nimport { fireEvent, HASSDomEvent } from \"../common/dom/fire_event\";\n\n// Allowed types are from iOS HIG.\n// https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/feedback/#haptics\n// Implementors on platforms other than iOS should attempt to match the patterns (shown in HIG) as closely as possible.\nexport type HapticType =\n | \"success\"\n | \"warning\"\n | \"failure\"\n | \"light\"\n | \"medium\"\n | \"heavy\"\n | \"selection\";\n\ndeclare global {\n // for fire event\n interface HASSDomEvents {\n haptic: HapticType;\n }\n\n interface GlobalEventHandlersEventMap {\n haptic: HASSDomEvent;\n }\n}\n\nexport const forwardHaptic = (hapticType: HapticType) => {\n fireEvent(window, \"haptic\", hapticType);\n};\n"],"sourceRoot":""} \ No newline at end of file diff --git a/supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js b/supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js new file mode 100644 index 000000000..9ba2f20dc --- /dev/null +++ b/supervisor/api/panel/chunk.ec48cf4e7f87d6417353.js @@ -0,0 +1,3 @@ +/*! For license information please see chunk.ec48cf4e7f87d6417353.js.LICENSE */ +(self.webpackJsonp=self.webpackJsonp||[]).push([[5],{181:function(e,t,n){"use strict";n.r(t);n(18),n(48),n(22),n(53),n(24),n(40);var r=n(5),o=n(117);"".concat(location.protocol,"//").concat(location.host);var i=n(10);n(55);function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function a(){var e=y(["\n ha-paper-dialog {\n min-width: 350px;\n font-size: 14px;\n border-radius: 2px;\n }\n app-toolbar {\n margin: 0;\n padding: 0 16px;\n color: var(--primary-text-color);\n background-color: var(--secondary-background-color);\n }\n app-toolbar [main-title] {\n margin-left: 16px;\n }\n ha-paper-dialog-scrollable {\n margin: 0;\n }\n paper-checkbox {\n display: block;\n margin: 4px;\n }\n @media all and (max-width: 450px), all and (max-height: 500px) {\n ha-paper-dialog {\n max-height: 100%;\n height: 100%;\n }\n app-toolbar {\n color: var(--text-primary-color);\n background-color: var(--primary-color);\n }\n }\n .details {\n color: var(--secondary-text-color);\n }\n .warning,\n .error {\n color: var(--google-red-500);\n }\n .buttons {\n display: flex;\n flex-direction: column;\n }\n .buttons li {\n list-style-type: none;\n }\n .buttons .icon {\n margin-right: 16px;\n }\n .no-margin-top {\n margin-top: 0;\n }\n "]);return a=function(){return e},e}function l(){var e=y(["\n
  • \n \n Wipe & restore\n \n
  • \n ']);return l=function(){return e},e}function c(){var e=y(['\n

    Error: ',"

    \n "]);return c=function(){return e},e}function d(){var e=y(['\n \n ',"\n \n "]);return p=function(){return e},e}function u(){var e=y(['\n
    Add-on:
    \n \n ',"\n \n "]);return u=function(){return e},e}function h(){var e=y(["\n \n ',"\n \n "]);return h=function(){return e},e}function f(){var e=y(['\n
    Folders:
    \n \n ',"\n \n "]);return f=function(){return e},e}function m(){var e=y(['\n \n \n \n
    ','
    \n
    \n
    \n ',"\n (",")
    \n ","\n
    \n
    Home Assistant:
    \n \n Home Assistant ',"\n \n ","\n ","\n ","\n ",'\n\n
    Actions:
    \n
      \n
    • \n \n \n Download Snapshot\n \n
    • \n
    • \n \n \n Restore Selected\n \n
    • \n ',"\n
    • \n \n Delete Snapshot\n \n
    • \n
    \n \n ']);return m=function(){return e},e}function g(){var e=y([""]);return g=function(){return e},e}function y(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}function b(e,t,n,r,o,i,s){try{var a=e[i](s),l=a.value}catch(c){return void n(c)}a.done?t(l):Promise.resolve(l).then(r,o)}function v(e){return function(){var t=this,n=arguments;return new Promise(function(r,o){var i=e.apply(t,n);function s(e){b(i,r,o,s,a,"next",e)}function a(e){b(i,r,o,s,a,"throw",e)}s(void 0)})}}function k(e){return(k=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function _(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function w(e,t){return(w=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function O(e){var t,n=P(e.key);"method"===e.kind?t={value:e.value,writable:!0,configurable:!0,enumerable:!1}:"get"===e.kind?t={get:e.value,configurable:!0,enumerable:!1}:"set"===e.kind?t={set:e.value,configurable:!0,enumerable:!1}:"field"===e.kind&&(t={configurable:!0,writable:!0,enumerable:!0});var r={kind:"field"===e.kind?"field":"method",key:n,placement:e.static?"static":"field"===e.kind?"own":"prototype",descriptor:t};return e.decorators&&(r.decorators=e.decorators),"field"===e.kind&&(r.initializer=e.value),r}function E(e,t){void 0!==e.descriptor.get?t.descriptor.get=e.descriptor.get:t.descriptor.set=e.descriptor.set}function x(e){return e.decorators&&e.decorators.length}function C(e){return void 0!==e&&!(void 0===e.value&&void 0===e.writable)}function j(e,t){var n=e[t];if(void 0!==n&&"function"!=typeof n)throw new TypeError("Expected '"+t+"' to be a function");return n}function P(e){var t=function(e,t){if("object"!==s(e)||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!==s(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===s(t)?t:String(t)}!function(e,t,n,r){var o=function(){var e={elementsDefinitionOrder:[["method"],["field"]],initializeInstanceElements:function(e,t){["method","field"].forEach(function(n){t.forEach(function(t){t.kind===n&&"own"===t.placement&&this.defineClassElement(e,t)},this)},this)},initializeClassElements:function(e,t){var n=e.prototype;["method","field"].forEach(function(r){t.forEach(function(t){var o=t.placement;if(t.kind===r&&("static"===o||"prototype"===o)){var i="static"===o?e:n;this.defineClassElement(i,t)}},this)},this)},defineClassElement:function(e,t){var n=t.descriptor;if("field"===t.kind){var r=t.initializer;n={enumerable:n.enumerable,writable:n.writable,configurable:n.configurable,value:void 0===r?void 0:r.call(e)}}Object.defineProperty(e,t.key,n)},decorateClass:function(e,t){var n=[],r=[],o={static:[],prototype:[],own:[]};if(e.forEach(function(e){this.addElementPlacement(e,o)},this),e.forEach(function(e){if(!x(e))return n.push(e);var t=this.decorateElement(e,o);n.push(t.element),n.push.apply(n,t.extras),r.push.apply(r,t.finishers)},this),!t)return{elements:n,finishers:r};var i=this.decorateConstructor(n,t);return r.push.apply(r,i.finishers),i.finishers=r,i},addElementPlacement:function(e,t,n){var r=t[e.placement];if(!n&&-1!==r.indexOf(e.key))throw new TypeError("Duplicated element ("+e.key+")");r.push(e.key)},decorateElement:function(e,t){for(var n=[],r=[],o=e.decorators,i=o.length-1;i>=0;i--){var s=t[e.placement];s.splice(s.indexOf(e.key),1);var a=this.fromElementDescriptor(e),l=this.toElementFinisherExtras((0,o[i])(a)||a);e=l.element,this.addElementPlacement(e,t),l.finisher&&r.push(l.finisher);var c=l.extras;if(c){for(var d=0;d=0;r--){var o=this.fromClassDescriptor(e),i=this.toClassDescriptor((0,t[r])(o)||o);if(void 0!==i.finisher&&n.push(i.finisher),void 0!==i.elements){e=i.elements;for(var s=0;st.name?1:-1}),this._addons=(n=this.snapshot.addons,n.map(function(e){return{slug:e.slug,name:e.name,version:e.version,checked:!0}})).sort(function(e,t){return e.name>t.name?1:-1}),this._dialogParams=t,e.prev=6,this._dialog.open(),e.next=14;break;case 10:return e.prev=10,e.t0=e.catch(6),e.next=14,this.showDialog(t);case 14:case"end":return e.stop()}var n,r,i},e,this,[[6,10]])}));return function(t){return e.apply(this,arguments)}}()},{kind:"method",key:"render",value:function(){var e=this;return this.snapshot?Object(r.e)(m(),this._dialogClosed,this._computeName,"full"===this.snapshot.type?"Full snapshot":"Partial snapshot",this._computeSize,this._formatDatetime(this.snapshot.date),this._restoreHass,function(t){return e._restoreHass=t.target.checked},this.snapshot.homeassistant,this._folders.length?Object(r.e)(f(),this._folders.map(function(t){return Object(r.e)(h(),t.checked,function(n){return e._updateFolders(t,n.target.checked)},t.name)})):"",this._addons.length?Object(r.e)(u(),this._addons.map(function(t){return Object(r.e)(p(),t.checked,function(n){return e._updateAddons(t,n.target.checked)},t.name)})):"",this.snapshot.protected?Object(r.e)(d(),this._passwordInput,this._snapshotPassword):"",this._error?Object(r.e)(c(),this._error):"",this._downloadClicked,this._partialRestoreClicked,"full"===this.snapshot.type?Object(r.e)(l(),this._fullRestoreClicked):"",this._deleteClicked):Object(r.e)(g())}},{kind:"get",static:!0,key:"styles",value:function(){return[i.c,Object(r.c)(a())]}},{kind:"method",key:"_updateFolders",value:function(e,t){this._folders=this._folders.map(function(n){return n.slug===e.slug&&(n.checked=t),n})}},{kind:"method",key:"_updateAddons",value:function(e,t){this._addons=this._addons.map(function(n){return n.slug===e.slug&&(n.checked=t),n})}},{kind:"method",key:"_passwordInput",value:function(e){this._snapshotPassword=e.detail.value}},{kind:"method",key:"_partialRestoreClicked",value:function(){var e=this;if(confirm("Are you sure you want to restore this snapshot?")){var t=this._addons.filter(function(e){return e.checked}).map(function(e){return e.slug}),n=this._folders.filter(function(e){return e.checked}).map(function(e){return e.slug}),r={homeassistant:this._restoreHass,addons:t,folders:n};this.snapshot.protected&&(r.password=this._snapshotPassword),this.hass.callApi("POST","hassio/snapshots/".concat(this.snapshot.slug,"/restore/partial"),r).then(function(){alert("Snapshot restored!"),e._dialog.close()},function(t){e._error=t.body.message})}}},{kind:"method",key:"_fullRestoreClicked",value:function(){var e=this;if(confirm("Are you sure you want to restore this snapshot?")){var t=this.snapshot.protected?{password:this._snapshotPassword}:void 0;this.hass.callApi("POST","hassio/snapshots/".concat(this.snapshot.slug,"/restore/full"),t).then(function(){alert("Snapshot restored!"),e._dialog.close()},function(t){e._error=t.body.message})}}},{kind:"method",key:"_deleteClicked",value:function(){var e=this;confirm("Are you sure you want to delete this snapshot?")&&this.hass.callApi("POST","hassio/snapshots/".concat(this.snapshot.slug,"/remove")).then(function(){e._dialog.close(),e._dialogParams.onDelete()},function(t){e._error=t.body.message})}},{kind:"method",key:"_downloadClicked",value:function(){var e=v(regeneratorRuntime.mark(function e(){var t,n,r;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,o=this.hass,i="/api/hassio/snapshots/".concat(this.snapshot.slug,"/download"),o.callWS({type:"auth/sign_path",path:i});case 3:t=e.sent,e.next=10;break;case 6:return e.prev=6,e.t0=e.catch(0),alert("Error: ".concat(e.t0.message)),e.abrupt("return");case 10:n=this._computeName.replace(/[^a-z0-9]+/gi,"_"),(r=document.createElement("a")).href=t.path,r.download="Hass_io_".concat(n,".tar"),this._dialog.appendChild(r),r.click(),this._dialog.removeChild(r);case 17:case"end":return e.stop()}var o,i},e,this,[[0,6]])}));return function(){return e.apply(this,arguments)}}()},{kind:"get",key:"_computeName",value:function(){return this.snapshot?this.snapshot.name||this.snapshot.slug:"Unnamed snapshot"}},{kind:"get",key:"_computeSize",value:function(){return Math.ceil(10*this.snapshot.size)/10+" MB"}},{kind:"method",key:"_formatDatetime",value:function(e){return new Date(e).toLocaleDateString(navigator.language,{weekday:"long",year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"})}},{kind:"method",key:"_dialogClosed",value:function(){this._dialogParams=void 0,this.snapshot=void 0,this._snapshotPassword="",this._folders=[],this._addons=[]}}]}},r.a)},31:function(e,t,n){"use strict";n.d(t,"b",function(){return i}),n.d(t,"a",function(){return s});n(4);var r=n(70),o=n(8),i={hostAttributes:{role:"dialog",tabindex:"-1"},properties:{modal:{type:Boolean,value:!1},__readied:{type:Boolean,value:!1}},observers:["_modalChanged(modal, __readied)"],listeners:{tap:"_onDialogClick"},ready:function(){this.__prevNoCancelOnOutsideClick=this.noCancelOnOutsideClick,this.__prevNoCancelOnEscKey=this.noCancelOnEscKey,this.__prevWithBackdrop=this.withBackdrop,this.__readied=!0},_modalChanged:function(e,t){t&&(e?(this.__prevNoCancelOnOutsideClick=this.noCancelOnOutsideClick,this.__prevNoCancelOnEscKey=this.noCancelOnEscKey,this.__prevWithBackdrop=this.withBackdrop,this.noCancelOnOutsideClick=!0,this.noCancelOnEscKey=!0,this.withBackdrop=!0):(this.noCancelOnOutsideClick=this.noCancelOnOutsideClick&&this.__prevNoCancelOnOutsideClick,this.noCancelOnEscKey=this.noCancelOnEscKey&&this.__prevNoCancelOnEscKey,this.withBackdrop=this.withBackdrop&&this.__prevWithBackdrop))},_updateClosingReasonConfirmed:function(e){this.closingReason=this.closingReason||{},this.closingReason.confirmed=e},_onDialogClick:function(e){for(var t=Object(o.a)(e).path,n=0,r=t.indexOf(this);n\n\n :host {\n display: block;\n @apply --layout-relative;\n }\n\n :host(.is-scrolled:not(:first-child))::before {\n content: \'\';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--divider-color);\n }\n\n :host(.can-scroll:not(.scrolled-to-bottom):not(:last-child))::after {\n content: \'\';\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 1px;\n background: var(--divider-color);\n }\n\n .scrollable {\n padding: 0 24px;\n\n @apply --layout-scroll;\n @apply --paper-dialog-scrollable;\n }\n\n .fit {\n @apply --layout-fit;\n }\n \n\n
    \n \n
    \n']);return s=function(){return e},e}Object(o.a)({_template:Object(i.a)(s()),is:"paper-dialog-scrollable",properties:{dialogElement:{type:Object}},get scrollTarget(){return this.$.scrollable},ready:function(){this._ensureTarget(),this.classList.add("no-padding")},attached:function(){this._ensureTarget(),requestAnimationFrame(this.updateScrollState.bind(this))},updateScrollState:function(){this.toggleClass("is-scrolled",this.scrollTarget.scrollTop>0),this.toggleClass("can-scroll",this.scrollTarget.offsetHeight=this.scrollTarget.scrollHeight)},_ensureTarget:function(){this.dialogElement=this.dialogElement||this.parentElement,this.dialogElement&&this.dialogElement.behaviors&&this.dialogElement.behaviors.indexOf(r.b)>=0?(this.dialogElement.sizingTarget=this.scrollTarget,this.scrollTarget.classList.remove("fit")):this.dialogElement&&this.scrollTarget.classList.add("fit")}})},55:function(e,t,n){"use strict";n(4),n(12),n(13),n(30),n(35);var r=document.createElement("template");r.setAttribute("style","display: none;"),r.innerHTML='\n \n',document.head.appendChild(r.content);var o=n(72),i=n(31),s=n(7),a=n(6);function l(){var e=function(e,t){t||(t=e.slice(0));return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}(['\n \n \n']);return l=function(){return e},e}Object(s.a)({_template:Object(a.a)(l()),is:"paper-dialog",behaviors:[i.a,o.a],listeners:{"neon-animation-finish":"_onNeonAnimationFinish"},_renderOpened:function(){this.cancelAnimation(),this.playAnimation("entry")},_renderClosed:function(){this.cancelAnimation(),this.playAnimation("exit")},_onNeonAnimationFinish:function(){this.opened?this._finishRenderOpened():this._finishRenderClosed()}});var c=n(59),d=n(8),p=n(67),u={getTabbableNodes:function(e){var t=[];return this._collectTabbableNodes(e,t)?p.a._sortByTabIndex(t):t},_collectTabbableNodes:function(e,t){if(e.nodeType!==Node.ELEMENT_NODE||!p.a._isVisible(e))return!1;var n,r=e,o=p.a._normalizedTabIndex(r),i=o>0;o>=0&&t.push(r),n="content"===r.localName||"slot"===r.localName?Object(d.a)(r).getDistributedNodes():Object(d.a)(r.shadowRoot||r.root||r).children;for(var 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 => hass.callWS({ type: \"auth/sign_path\", path });\n\nexport const fetchAuthProviders = () =>\n fetch(\"/auth/providers\", {\n credentials: \"same-origin\",\n });\n\nexport const createAuthForUser = async (\n hass: HomeAssistant,\n userId: string,\n username: string,\n password: string\n) =>\n hass.callWS({\n type: \"config/auth_provider/homeassistant/create\",\n user_id: userId,\n username,\n password,\n });\n","import \"@material/mwc-button\";\nimport \"@polymer/app-layout/app-toolbar/app-toolbar\";\nimport \"@polymer/iron-icon/iron-icon\";\nimport \"@polymer/paper-dialog-scrollable/paper-dialog-scrollable\";\nimport \"@polymer/paper-icon-button/paper-icon-button\";\nimport \"@polymer/paper-input/paper-input\";\nimport { PaperDialogElement } from \"@polymer/paper-dialog\";\nimport { PaperCheckboxElement } from \"@polymer/paper-checkbox/paper-checkbox\";\nimport {\n css,\n CSSResult,\n customElement,\n html,\n LitElement,\n property,\n TemplateResult,\n query,\n} from \"lit-element\";\n\nimport {\n fetchHassioSnapshotInfo,\n HassioSnapshotDetail,\n} from \"../../../../src/data/hassio/snapshot\";\nimport { getSignedPath } from \"../../../../src/data/auth\";\nimport { HassioSnapshotDialogParams } from \"./show-dialog-hassio-snapshot\";\nimport { haStyleDialog } from \"../../../../src/resources/styles\";\nimport { HomeAssistant } from \"../../../../src/types\";\nimport { PolymerChangedEvent } from \"../../../../src/polymer-types\";\n\nimport \"../../../../src/components/dialog/ha-paper-dialog\";\n\nconst _computeFolders = (folders) => {\n const list: Array<{ slug: string; name: string; checked: boolean }> = [];\n if (folders.includes(\"homeassistant\")) {\n list.push({\n slug: \"homeassistant\",\n name: \"Home Assistant configuration\",\n checked: true,\n });\n }\n if (folders.includes(\"ssl\")) {\n list.push({ slug: \"ssl\", name: \"SSL\", checked: true });\n }\n if (folders.includes(\"share\")) {\n list.push({ slug: \"share\", name: \"Share\", checked: true });\n }\n if (folders.includes(\"addons/local\")) {\n list.push({ slug: \"addons/local\", name: \"Local add-ons\", checked: true });\n }\n return list;\n};\n\nconst _computeAddons = (addons) => {\n return addons.map((addon) => ({\n slug: addon.slug,\n name: addon.name,\n version: addon.version,\n checked: true,\n }));\n};\n\ninterface AddonItem {\n slug: string;\n name: string;\n version: string;\n checked: boolean | null | undefined;\n}\n\ninterface FolderItem {\n slug: string;\n name: string;\n checked: boolean | null | undefined;\n}\n\n@customElement(\"dialog-hassio-snapshot\")\nclass HassioSnapshotDialog extends LitElement {\n @property() public hass!: HomeAssistant;\n @property() private _error?: string;\n @property() private snapshot?: HassioSnapshotDetail;\n @property() private _folders!: FolderItem[];\n @property() private _addons!: AddonItem[];\n @property() private _dialogParams?: HassioSnapshotDialogParams;\n @property() private _snapshotPassword!: string;\n @property() private _restoreHass: boolean | null | undefined = true;\n @query(\"#dialog\") private _dialog!: PaperDialogElement;\n\n public async showDialog(params: HassioSnapshotDialogParams) {\n this.snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);\n this._folders = _computeFolders(\n this.snapshot.folders\n ).sort((a: FolderItem, b: FolderItem) => (a.name > b.name ? 1 : -1));\n this._addons = _computeAddons(\n this.snapshot.addons\n ).sort((a: AddonItem, b: AddonItem) => (a.name > b.name ? 1 : -1));\n\n this._dialogParams = params;\n\n try {\n this._dialog.open();\n } catch {\n await this.showDialog(params);\n }\n }\n\n protected render(): TemplateResult {\n if (!this.snapshot) {\n return html``;\n }\n return html`\n \n \n \n
    ${this._computeName}
    \n
    \n
    \n ${this.snapshot.type === \"full\"\n ? \"Full snapshot\"\n : \"Partial snapshot\"}\n (${this._computeSize})
    \n ${this._formatDatetime(this.snapshot.date)}\n
    \n
    Home Assistant:
    \n \n (this._restoreHass = (ev.target as PaperCheckboxElement).checked)}\"\n >\n Home Assistant ${this.snapshot.homeassistant}\n \n ${this._folders.length\n ? html`\n
    Folders:
    \n \n ${this._folders.map((item) => {\n return html`\n \n this._updateFolders(\n item,\n (ev.target as PaperCheckboxElement).checked\n )}\"\n >\n ${item.name}\n \n `;\n })}\n \n `\n : \"\"}\n ${this._addons.length\n ? html`\n
    Add-on:
    \n \n ${this._addons.map((item) => {\n return html`\n \n this._updateAddons(\n item,\n (ev.target as PaperCheckboxElement).checked\n )}\"\n >\n ${item.name}\n \n `;\n })}\n \n `\n : \"\"}\n ${this.snapshot.protected\n ? html`\n \n `\n : \"\"}\n ${this._error\n ? html`\n

    Error: ${this._error}

    \n `\n : \"\"}\n\n
    Actions:
    \n
      \n
    • \n \n \n Download Snapshot\n \n
    • \n
    • \n \n \n Restore Selected\n \n
    • \n ${this.snapshot.type === \"full\"\n ? html`\n
    • \n \n \n Wipe & restore\n \n
    • \n `\n : \"\"}\n
    • \n \n \n Delete Snapshot\n \n
    • \n
    \n \n `;\n }\n\n static get styles(): CSSResult[] {\n return [\n haStyleDialog,\n css`\n ha-paper-dialog {\n min-width: 350px;\n font-size: 14px;\n border-radius: 2px;\n }\n app-toolbar {\n margin: 0;\n padding: 0 16px;\n color: var(--primary-text-color);\n background-color: var(--secondary-background-color);\n }\n app-toolbar [main-title] {\n margin-left: 16px;\n }\n ha-paper-dialog-scrollable {\n margin: 0;\n }\n paper-checkbox {\n display: block;\n margin: 4px;\n }\n @media all and (max-width: 450px), all and (max-height: 500px) {\n ha-paper-dialog {\n max-height: 100%;\n height: 100%;\n }\n app-toolbar {\n color: var(--text-primary-color);\n background-color: var(--primary-color);\n }\n }\n .details {\n color: var(--secondary-text-color);\n }\n .warning,\n .error {\n color: var(--google-red-500);\n }\n .buttons {\n display: flex;\n flex-direction: column;\n }\n .buttons li {\n list-style-type: none;\n }\n .buttons .icon {\n margin-right: 16px;\n }\n .no-margin-top {\n margin-top: 0;\n }\n `,\n ];\n }\n\n private _updateFolders(item: FolderItem, value: boolean | null | undefined) {\n this._folders = this._folders.map((folder) => {\n if (folder.slug === item.slug) {\n folder.checked = value;\n }\n return folder;\n });\n }\n\n private _updateAddons(item: AddonItem, value: boolean | null | undefined) {\n this._addons = this._addons.map((addon) => {\n if (addon.slug === item.slug) {\n addon.checked = value;\n }\n return addon;\n });\n }\n\n private _passwordInput(ev: PolymerChangedEvent) {\n this._snapshotPassword = ev.detail.value;\n }\n\n private _partialRestoreClicked() {\n if (!confirm(\"Are you sure you want to restore this snapshot?\")) {\n return;\n }\n\n const addons = this._addons\n .filter((addon) => addon.checked)\n .map((addon) => addon.slug);\n\n const folders = this._folders\n .filter((folder) => folder.checked)\n .map((folder) => folder.slug);\n\n const data: {\n homeassistant: boolean | null | undefined;\n addons: any;\n folders: any;\n password?: string;\n } = {\n homeassistant: this._restoreHass,\n addons,\n folders,\n };\n\n if (this.snapshot!.protected) {\n data.password = this._snapshotPassword;\n }\n\n this.hass\n .callApi(\n \"POST\",\n\n `hassio/snapshots/${this.snapshot!.slug}/restore/partial`,\n data\n )\n .then(\n () => {\n alert(\"Snapshot restored!\");\n this._dialog.close();\n },\n (error) => {\n this._error = error.body.message;\n }\n );\n }\n\n private _fullRestoreClicked() {\n if (!confirm(\"Are you sure you want to restore this snapshot?\")) {\n return;\n }\n\n const data = this.snapshot!.protected\n ? { password: this._snapshotPassword }\n : undefined;\n\n this.hass\n .callApi(\n \"POST\",\n `hassio/snapshots/${this.snapshot!.slug}/restore/full`,\n data\n )\n .then(\n () => {\n alert(\"Snapshot restored!\");\n this._dialog.close();\n },\n (error) => {\n this._error = error.body.message;\n }\n );\n }\n\n private _deleteClicked() {\n if (!confirm(\"Are you sure you want to delete this snapshot?\")) {\n return;\n }\n\n this.hass\n\n .callApi(\"POST\", `hassio/snapshots/${this.snapshot!.slug}/remove`)\n .then(\n () => {\n this._dialog.close();\n this._dialogParams!.onDelete();\n },\n (error) => {\n this._error = error.body.message;\n }\n );\n }\n\n private async _downloadClicked() {\n let signedPath: { path: string };\n try {\n signedPath = await getSignedPath(\n this.hass,\n `/api/hassio/snapshots/${this.snapshot!.slug}/download`\n );\n } catch (err) {\n alert(`Error: ${err.message}`);\n return;\n }\n\n const name = this._computeName.replace(/[^a-z0-9]+/gi, \"_\");\n const a = document.createElement(\"a\");\n a.href = signedPath.path;\n a.download = `Hass_io_${name}.tar`;\n this._dialog.appendChild(a);\n a.click();\n this._dialog.removeChild(a);\n }\n\n private get _computeName() {\n return this.snapshot\n ? this.snapshot.name || this.snapshot.slug\n : \"Unnamed snapshot\";\n }\n\n private get _computeSize() {\n return Math.ceil(this.snapshot!.size * 10) / 10 + \" MB\";\n }\n\n private _formatDatetime(datetime) {\n return new Date(datetime).toLocaleDateString(navigator.language, {\n weekday: \"long\",\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n });\n }\n\n private _dialogClosed() {\n this._dialogParams = undefined;\n this.snapshot = undefined;\n this._snapshotPassword = \"\";\n this._folders = [];\n this._addons = [];\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"dialog-hassio-snapshot\": HassioSnapshotDialog;\n }\n}\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\n\nimport {IronOverlayBehavior} from '@polymer/iron-overlay-behavior/iron-overlay-behavior.js';\nimport {dom} from '@polymer/polymer/lib/legacy/polymer.dom.js';\n\n/**\n Use `Polymer.PaperDialogBehavior` and `paper-dialog-shared-styles.html` to\n implement a Material Design dialog.\n\n For example, if `` implements this behavior:\n\n \n

    Header

    \n
    Dialog body
    \n
    \n Cancel\n Accept\n
    \n
    \n\n `paper-dialog-shared-styles.html` provide styles for a header, content area,\n and an action area for buttons. Use the `

    ` tag for the header and the\n `buttons` class for the action area. You can use the `paper-dialog-scrollable`\n element (in its own repository) if you need a scrolling content area.\n\n Use the `dialog-dismiss` and `dialog-confirm` attributes on interactive\n controls to close the dialog. If the user dismisses the dialog with\n `dialog-confirm`, the `closingReason` will update to include `confirmed:\n true`.\n\n ### Accessibility\n\n This element has `role=\"dialog\"` by default. Depending on the context, it may\n be more appropriate to override this attribute with `role=\"alertdialog\"`.\n\n If `modal` is set, the element will prevent the focus from exiting the\n element. It will also ensure that focus remains in the dialog.\n\n @hero hero.svg\n @demo demo/index.html\n @polymerBehavior PaperDialogBehavior\n */\nexport const PaperDialogBehaviorImpl = {\n\n hostAttributes: {'role': 'dialog', 'tabindex': '-1'},\n\n properties: {\n\n /**\n * If `modal` is true, this implies `no-cancel-on-outside-click`,\n * `no-cancel-on-esc-key` and `with-backdrop`.\n */\n modal: {type: Boolean, value: false},\n\n __readied: {type: Boolean, value: false}\n\n },\n\n observers: ['_modalChanged(modal, __readied)'],\n\n listeners: {'tap': '_onDialogClick'},\n\n /**\n * @return {void}\n */\n ready: function() {\n // Only now these properties can be read.\n this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;\n this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;\n this.__prevWithBackdrop = this.withBackdrop;\n this.__readied = true;\n },\n\n _modalChanged: function(modal, readied) {\n // modal implies noCancelOnOutsideClick, noCancelOnEscKey and withBackdrop.\n // We need to wait for the element to be ready before we can read the\n // properties values.\n if (!readied) {\n return;\n }\n\n if (modal) {\n this.__prevNoCancelOnOutsideClick = this.noCancelOnOutsideClick;\n this.__prevNoCancelOnEscKey = this.noCancelOnEscKey;\n this.__prevWithBackdrop = this.withBackdrop;\n this.noCancelOnOutsideClick = true;\n this.noCancelOnEscKey = true;\n this.withBackdrop = true;\n } else {\n // If the value was changed to false, let it false.\n this.noCancelOnOutsideClick =\n this.noCancelOnOutsideClick && this.__prevNoCancelOnOutsideClick;\n this.noCancelOnEscKey =\n this.noCancelOnEscKey && this.__prevNoCancelOnEscKey;\n this.withBackdrop = this.withBackdrop && this.__prevWithBackdrop;\n }\n },\n\n _updateClosingReasonConfirmed: function(confirmed) {\n this.closingReason = this.closingReason || {};\n this.closingReason.confirmed = confirmed;\n },\n\n /**\n * Will dismiss the dialog if user clicked on an element with dialog-dismiss\n * or dialog-confirm attribute.\n */\n _onDialogClick: function(event) {\n // Search for the element with dialog-confirm or dialog-dismiss,\n // from the root target until this (excluded).\n var path = dom(event).path;\n for (var i = 0, l = path.indexOf(this); i < l; i++) {\n var target = path[i];\n if (target.hasAttribute &&\n (target.hasAttribute('dialog-dismiss') ||\n target.hasAttribute('dialog-confirm'))) {\n this._updateClosingReasonConfirmed(\n target.hasAttribute('dialog-confirm'));\n this.close();\n event.stopPropagation();\n break;\n }\n }\n }\n\n};\n\n/** @polymerBehavior */\nexport const PaperDialogBehavior =\n [IronOverlayBehavior, PaperDialogBehaviorImpl];\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\nimport '@polymer/paper-styles/default-theme.js';\n\nimport {PaperDialogBehaviorImpl} from '@polymer/paper-dialog-behavior/paper-dialog-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/**\nMaterial design:\n[Dialogs](https://www.google.com/design/spec/components/dialogs.html)\n\n`paper-dialog-scrollable` implements a scrolling area used in a Material Design\ndialog. It shows a divider at the top and/or bottom indicating more content,\ndepending on scroll position. Use this together with elements implementing\n`Polymer.PaperDialogBehavior`.\n\n \n

    Header

    \n \n Lorem ipsum...\n \n
    \n OK\n
    \n
    \n\nIt shows a top divider after scrolling if it is not the first child in its\nparent container, indicating there is more content above. It shows a bottom\ndivider if it is scrollable and it is not the last child in its parent\ncontainer, indicating there is more content below. The bottom divider is hidden\nif it is scrolled to the bottom.\n\nIf `paper-dialog-scrollable` is not a direct child of the element implementing\n`Polymer.PaperDialogBehavior`, remember to set the `dialogElement`:\n\n \n

    Header

    \n
    \n

    Sub-header

    \n \n Lorem ipsum...\n \n
    \n
    \n OK\n
    \n
    \n\n \n\n### Styling\nThe following custom properties and mixins are available for styling:\n\nCustom property | Description | Default\n----------------|-------------|----------\n`--paper-dialog-scrollable` | Mixin for the scrollable content | {}\n\n@group Paper Elements\n@element paper-dialog-scrollable\n@demo demo/index.html\n@hero hero.svg\n*/\nPolymer({\n _template: html`\n \n\n
    \n \n
    \n`,\n\n is: 'paper-dialog-scrollable',\n\n properties: {\n\n /**\n * The dialog element that implements `Polymer.PaperDialogBehavior`\n * containing this element.\n * @type {?Node}\n */\n dialogElement: {type: Object}\n\n },\n\n /**\n * Returns the scrolling element.\n */\n get scrollTarget() {\n return this.$.scrollable;\n },\n\n ready: function() {\n this._ensureTarget();\n this.classList.add('no-padding');\n },\n\n attached: function() {\n this._ensureTarget();\n requestAnimationFrame(this.updateScrollState.bind(this));\n },\n\n updateScrollState: function() {\n this.toggleClass('is-scrolled', this.scrollTarget.scrollTop > 0);\n this.toggleClass(\n 'can-scroll',\n this.scrollTarget.offsetHeight < this.scrollTarget.scrollHeight);\n this.toggleClass(\n 'scrolled-to-bottom',\n this.scrollTarget.scrollTop + this.scrollTarget.offsetHeight >=\n this.scrollTarget.scrollHeight);\n },\n\n _ensureTarget: function() {\n // Read parentElement instead of parentNode in order to skip shadowRoots.\n this.dialogElement = this.dialogElement || this.parentElement;\n // Check if dialog implements paper-dialog-behavior. If not, fit\n // scrollTarget to host.\n if (this.dialogElement && this.dialogElement.behaviors &&\n this.dialogElement.behaviors.indexOf(PaperDialogBehaviorImpl) >= 0) {\n this.dialogElement.sizingTarget = this.scrollTarget;\n this.scrollTarget.classList.remove('fit');\n } else if (this.dialogElement) {\n this.scrollTarget.classList.add('fit');\n }\n }\n});\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\n/*\n### Styling\n\nThe following custom properties and mixins are available for styling.\n\nCustom property | Description | Default\n----------------|-------------|----------\n`--paper-dialog-background-color` | Dialog background color | `--primary-background-color`\n`--paper-dialog-color` | Dialog foreground color | `--primary-text-color`\n`--paper-dialog` | Mixin applied to the dialog | `{}`\n`--paper-dialog-title` | Mixin applied to the title (`

    `) element | `{}`\n`--paper-dialog-button-color` | Button area foreground color | `--default-primary-color`\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/iron-flex-layout/iron-flex-layout.js';\nimport '@polymer/paper-styles/default-theme.js';\nimport '@polymer/paper-styles/typography.js';\nimport '@polymer/paper-styles/shadow.js';\nconst $_documentContainer = document.createElement('template');\n$_documentContainer.setAttribute('style', 'display: none;');\n\n$_documentContainer.innerHTML = `\n \n`;\n\ndocument.head.appendChild($_documentContainer.content);\n","/**\n@license\nCopyright (c) 2015 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\nimport '@polymer/polymer/polymer-legacy.js';\nimport '@polymer/paper-dialog-behavior/paper-dialog-shared-styles.js';\n\nimport {NeonAnimationRunnerBehavior} from '@polymer/neon-animation/neon-animation-runner-behavior.js';\nimport {PaperDialogBehavior} from '@polymer/paper-dialog-behavior/paper-dialog-behavior.js';\nimport {Polymer} from '@polymer/polymer/lib/legacy/polymer-fn.js';\nimport {html} from '@polymer/polymer/lib/utils/html-tag.js';\n\n/**\nMaterial design:\n[Dialogs](https://www.google.com/design/spec/components/dialogs.html)\n\n`` is a dialog with Material Design styling and optional\nanimations when it is opened or closed. It provides styles for a header, content\narea, and an action area for buttons. You can use the\n`` element (in its own repository) if you need a\nscrolling content area. To autofocus a specific child element after opening the\ndialog, give it the `autofocus` attribute. See `Polymer.PaperDialogBehavior` and\n`Polymer.IronOverlayBehavior` for specifics.\n\nFor example, the following code implements a dialog with a header, scrolling\ncontent area and buttons. Focus will be given to the `dialog-confirm` button\nwhen the dialog is opened.\n\n \n

    Header

    \n \n Lorem ipsum...\n \n
    \n Cancel\n Accept\n
    \n
    \n\n### Styling\n\nSee the docs for `Polymer.PaperDialogBehavior` for the custom properties\navailable for styling this element.\n\n### Animations\n\nSet the `entry-animation` and/or `exit-animation` attributes to add an animation\nwhen the dialog is opened or closed. See the documentation in\n[PolymerElements/neon-animation](https://github.com/PolymerElements/neon-animation)\nfor more info.\n\nFor example:\n\n \n\n \n

    Header

    \n
    Dialog body
    \n
    \n\n### Accessibility\n\nSee the docs for `Polymer.PaperDialogBehavior` for accessibility features\nimplemented by this element.\n\n@group Paper Elements\n@element paper-dialog\n@hero hero.svg\n@demo demo/index.html\n*/\nPolymer({\n _template: html`\n \n \n`,\n\n is: 'paper-dialog',\n behaviors: [PaperDialogBehavior, NeonAnimationRunnerBehavior],\n listeners: {'neon-animation-finish': '_onNeonAnimationFinish'},\n\n _renderOpened: function() {\n this.cancelAnimation();\n this.playAnimation('entry');\n },\n\n _renderClosed: function() {\n this.cancelAnimation();\n this.playAnimation('exit');\n },\n\n _onNeonAnimationFinish: function() {\n if (this.opened) {\n this._finishRenderOpened();\n } else {\n this._finishRenderClosed();\n }\n }\n});\n","/**\n@license\nCopyright (c) 2016 The Polymer Project Authors. All rights reserved.\nThis code may only be used under the BSD style license found at\nhttp://polymer.github.io/LICENSE.txt The complete set of authors may be found at\nhttp://polymer.github.io/AUTHORS.txt The complete set of contributors may be\nfound at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as\npart of the polymer project is also subject to an additional IP rights grant\nfound at http://polymer.github.io/PATENTS.txt\n*/\n/*\n Fixes issue with not using shadow dom properly in iron-overlay-behavior/icon-focusables-helper.js\n*/\nimport { dom } from \"@polymer/polymer/lib/legacy/polymer.dom.js\";\n\nimport { IronFocusablesHelper } from \"@polymer/iron-overlay-behavior/iron-focusables-helper.js\";\n\nexport const HaIronFocusablesHelper = {\n /**\n * Returns a sorted array of tabbable nodes, including the root node.\n * It searches the tabbable nodes in the light and shadow dom of the chidren,\n * sorting the result by tabindex.\n * @param {!Node} node\n * @return {!Array}\n */\n getTabbableNodes: function(node) {\n var result = [];\n // If there is at least one element with tabindex > 0, we need to sort\n // the final array by tabindex.\n var needsSortByTabIndex = this._collectTabbableNodes(node, result);\n if (needsSortByTabIndex) {\n return IronFocusablesHelper._sortByTabIndex(result);\n }\n return result;\n },\n\n /**\n * Searches for nodes that are tabbable and adds them to the `result` array.\n * Returns if the `result` array needs to be sorted by tabindex.\n * @param {!Node} node The starting point for the search; added to `result`\n * if tabbable.\n * @param {!Array} result\n * @return {boolean}\n * @private\n */\n _collectTabbableNodes: function(node, result) {\n // If not an element or not visible, no need to explore children.\n if (\n node.nodeType !== Node.ELEMENT_NODE ||\n !IronFocusablesHelper._isVisible(node)\n ) {\n return false;\n }\n var element = /** @type {!HTMLElement} */ (node);\n var tabIndex = IronFocusablesHelper._normalizedTabIndex(element);\n var needsSort = tabIndex > 0;\n if (tabIndex >= 0) {\n result.push(element);\n }\n\n // In ShadowDOM v1, tab order is affected by the order of distrubution.\n // E.g. getTabbableNodes(#root) in ShadowDOM v1 should return [#A, #B];\n // in ShadowDOM v0 tab order is not affected by the distrubution order,\n // in fact getTabbableNodes(#root) returns [#B, #A].\n //
    \n // \n // \n // \n // \n // \n // \n //
    \n // TODO(valdrin) support ShadowDOM v1 when upgrading to Polymer v2.0.\n var children;\n if (element.localName === \"content\" || element.localName === \"slot\") {\n children = dom(element).getDistributedNodes();\n } else {\n // /////////////////////////\n // Use shadow root if possible, will check for distributed nodes.\n // THIS IS THE CHANGED LINE\n children = dom(element.shadowRoot || element.root || element).children;\n // /////////////////////////\n }\n for (var i = 0; i < children.length; i++) {\n // Ensure method is always invoked to collect tabbable children.\n needsSort = this._collectTabbableNodes(children[i], result) || needsSort;\n }\n return needsSort;\n },\n};\n","import \"@polymer/paper-dialog/paper-dialog\";\nimport { mixinBehaviors } from \"@polymer/polymer/lib/legacy/class\";\nimport { HaIronFocusablesHelper } from \"./ha-iron-focusables-helper.js\";\n// tslint:disable-next-line\nimport { PaperDialogElement } from \"@polymer/paper-dialog/paper-dialog\";\n\nconst paperDialogClass = customElements.get(\"paper-dialog\");\n\n// behavior that will override existing iron-overlay-behavior and call the fixed implementation\nconst haTabFixBehaviorImpl = {\n get _focusableNodes() {\n return HaIronFocusablesHelper.getTabbableNodes(this);\n },\n};\n\n// paper-dialog that uses the haTabFixBehaviorImpl behvaior\n// export class HaPaperDialog extends paperDialogClass {}\n// @ts-ignore\nexport class HaPaperDialog\n extends mixinBehaviors([haTabFixBehaviorImpl], paperDialogClass)\n implements PaperDialogElement {}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ha-paper-dialog\": HaPaperDialog;\n }\n}\ncustomElements.define(\"ha-paper-dialog\", HaPaperDialog);\n"],"sourceRoot":""} \ No newline at end of file diff --git a/supervisor/api/panel/entrypoint.js b/supervisor/api/panel/entrypoint.js index d3c630c08..ab3432c8c 100644 --- a/supervisor/api/panel/entrypoint.js +++ b/supervisor/api/panel/entrypoint.js @@ -1,2 +1,2 @@ -!function(e){function n(n){for(var t,o,a=n[0],i=n[1],f=0,u=[]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 diff --git a/supervisor/api/panel/entrypoint.js.map b/supervisor/api/panel/entrypoint.js.map index aa03c226f..2b49080f3 100644 --- a/supervisor/api/panel/entrypoint.js.map +++ b/supervisor/api/panel/entrypoint.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./hassio/src/entrypoint.ts"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","installedModules","6","__webpack_require__","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","p","0","1","2","3","4","5","7","8","9","10","11","12","13","14","15","jsonpScriptSrc","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","self","oldJsonpFunction","slice","s","window","loadES5Adapter","then","styleEl","innerHTML"],"mappings":"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","file":"entrypoint.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t};\n\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t6: 0\n \t};\n\n\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"chunk.\" + {\"0\":\"7ab69340d9bb40dda79e\",\"1\":\"610423483939ffdbfb92\",\"2\":\"ece210525014eb36073b\",\"3\":\"a947da09b1ace1352383\",\"4\":\"d402ad0de6ea5da0f9fc\",\"5\":\"05d0c315f85f2fc8c34a\",\"7\":\"b38bff4d0774d202dcad\",\"8\":\"af76a4db9eb1e2862aae\",\"9\":\"54be96e339b8c82764a0\",\"10\":\"045f3417b78f858fff44\",\"11\":\"381907f8b6a21cec010f\",\"12\":\"b2dce600432c76a53d8c\",\"13\":\"70a435e100109291f210\",\"14\":\"594ef9f33131c4d33409\",\"15\":\"7ac9df67ea34d63f8998\"}[chunkId] + \".js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/api/hassio/app/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = self[\"webpackJsonp\"] = self[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","window.loadES5Adapter().then(() => {\n // eslint-disable-next-line\n import(/* webpackChunkName: \"roboto\" */ \"../../src/resources/roboto\");\n // eslint-disable-next-line\n import(/* webpackChunkName: \"hassio-icons\" */ \"./resources/hassio-icons\");\n // eslint-disable-next-line\n import(/* webpackChunkName: \"hassio-main\" */ \"./hassio-main\");\n});\n\nconst styleEl = document.createElement(\"style\");\nstyleEl.innerHTML = `\nbody {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-weight: 400;\n margin: 0;\n padding: 0;\n height: 100vh;\n}\n`;\ndocument.head.appendChild(styleEl);\n"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./hassio/src/entrypoint.ts"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","installedModules","6","__webpack_require__","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","p","0","1","2","3","4","5","7","8","9","10","11","12","13","14","15","jsonpScriptSrc","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","self","oldJsonpFunction","slice","s","window","loadES5Adapter","then","styleEl","innerHTML"],"mappings":"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","file":"entrypoint.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t};\n\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t6: 0\n \t};\n\n\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"chunk.\" + {\"0\":\"7ab69340d9bb40dda79e\",\"1\":\"610423483939ffdbfb92\",\"2\":\"ece210525014eb36073b\",\"3\":\"d9ebbcff54094bd6592a\",\"4\":\"d402ad0de6ea5da0f9fc\",\"5\":\"ec48cf4e7f87d6417353\",\"7\":\"b38bff4d0774d202dcad\",\"8\":\"af76a4db9eb1e2862aae\",\"9\":\"54be96e339b8c82764a0\",\"10\":\"05403bc1774dc39117f6\",\"11\":\"381907f8b6a21cec010f\",\"12\":\"b2dce600432c76a53d8c\",\"13\":\"70a435e100109291f210\",\"14\":\"594ef9f33131c4d33409\",\"15\":\"7ac9df67ea34d63f8998\"}[chunkId] + \".js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/api/hassio/app/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = self[\"webpackJsonp\"] = self[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","window.loadES5Adapter().then(() => {\n // eslint-disable-next-line\n import(/* webpackChunkName: \"roboto\" */ \"../../src/resources/roboto\");\n // eslint-disable-next-line\n import(/* webpackChunkName: \"hassio-icons\" */ \"./resources/hassio-icons\");\n // eslint-disable-next-line\n import(/* webpackChunkName: \"hassio-main\" */ \"./hassio-main\");\n});\n\nconst styleEl = document.createElement(\"style\");\nstyleEl.innerHTML = `\nbody {\n font-family: Roboto, sans-serif;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-weight: 400;\n margin: 0;\n padding: 0;\n height: 100vh;\n}\n`;\ndocument.head.appendChild(styleEl);\n"],"sourceRoot":""} \ No newline at end of file diff --git a/supervisor/api/panel/manifest.json b/supervisor/api/panel/manifest.json index ccb7dc7d5..96f5a66a8 100644 --- a/supervisor/api/panel/manifest.json +++ b/supervisor/api/panel/manifest.json @@ -5,12 +5,12 @@ "vendors~hassio-icons~hassio-main.js.map": "/api/hassio/app/chunk.610423483939ffdbfb92.js.map", "codemirror.js": "/api/hassio/app/chunk.ece210525014eb36073b.js", "codemirror.js.map": "/api/hassio/app/chunk.ece210525014eb36073b.js.map", - "confirmation.js": "/api/hassio/app/chunk.a947da09b1ace1352383.js", - "confirmation.js.map": "/api/hassio/app/chunk.a947da09b1ace1352383.js.map", + "confirmation.js": "/api/hassio/app/chunk.d9ebbcff54094bd6592a.js", + "confirmation.js.map": "/api/hassio/app/chunk.d9ebbcff54094bd6592a.js.map", "dialog-hassio-markdown.js": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js", "dialog-hassio-markdown.js.map": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js.map", - "dialog-hassio-snapshot.js": "/api/hassio/app/chunk.05d0c315f85f2fc8c34a.js", - "dialog-hassio-snapshot.js.map": "/api/hassio/app/chunk.05d0c315f85f2fc8c34a.js.map", + "dialog-hassio-snapshot.js": "/api/hassio/app/chunk.ec48cf4e7f87d6417353.js", + "dialog-hassio-snapshot.js.map": "/api/hassio/app/chunk.ec48cf4e7f87d6417353.js.map", "entrypoint.js": "/api/hassio/app/entrypoint.js", "entrypoint.js.map": "/api/hassio/app/entrypoint.js.map", "hassio-addon-view.js": "/api/hassio/app/chunk.b38bff4d0774d202dcad.js", @@ -19,8 +19,8 @@ "hassio-icons.js.map": "/api/hassio/app/chunk.af76a4db9eb1e2862aae.js.map", "hassio-ingress-view.js": "/api/hassio/app/chunk.54be96e339b8c82764a0.js", "hassio-ingress-view.js.map": "/api/hassio/app/chunk.54be96e339b8c82764a0.js.map", - "hassio-main.js": "/api/hassio/app/chunk.045f3417b78f858fff44.js", - "hassio-main.js.map": "/api/hassio/app/chunk.045f3417b78f858fff44.js.map", + "hassio-main.js": "/api/hassio/app/chunk.05403bc1774dc39117f6.js", + "hassio-main.js.map": "/api/hassio/app/chunk.05403bc1774dc39117f6.js.map", "mdi-icons.js": "/api/hassio/app/chunk.381907f8b6a21cec010f.js", "mdi-icons.js.map": "/api/hassio/app/chunk.381907f8b6a21cec010f.js.map", "roboto.js": "/api/hassio/app/chunk.b2dce600432c76a53d8c.js", @@ -33,11 +33,11 @@ "vendors~hassio-main.js.map": "/api/hassio/app/chunk.7ac9df67ea34d63f8998.js.map", "a1ebfa0a88593a3b571c.worker.js": "/api/hassio/app/a1ebfa0a88593a3b571c.worker.js", "a1ebfa0a88593a3b571c.worker.js.map": "/api/hassio/app/a1ebfa0a88593a3b571c.worker.js.map", - "chunk.05d0c315f85f2fc8c34a.js.LICENSE": "/api/hassio/app/chunk.05d0c315f85f2fc8c34a.js.LICENSE", "chunk.594ef9f33131c4d33409.js.LICENSE": "/api/hassio/app/chunk.594ef9f33131c4d33409.js.LICENSE", "chunk.610423483939ffdbfb92.js.LICENSE": "/api/hassio/app/chunk.610423483939ffdbfb92.js.LICENSE", "chunk.7ab69340d9bb40dda79e.js.LICENSE": "/api/hassio/app/chunk.7ab69340d9bb40dda79e.js.LICENSE", "chunk.7ac9df67ea34d63f8998.js.LICENSE": "/api/hassio/app/chunk.7ac9df67ea34d63f8998.js.LICENSE", - "chunk.a947da09b1ace1352383.js.LICENSE": "/api/hassio/app/chunk.a947da09b1ace1352383.js.LICENSE", - "chunk.d402ad0de6ea5da0f9fc.js.LICENSE": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js.LICENSE" + "chunk.d402ad0de6ea5da0f9fc.js.LICENSE": "/api/hassio/app/chunk.d402ad0de6ea5da0f9fc.js.LICENSE", + "chunk.d9ebbcff54094bd6592a.js.LICENSE": "/api/hassio/app/chunk.d9ebbcff54094bd6592a.js.LICENSE", + "chunk.ec48cf4e7f87d6417353.js.LICENSE": "/api/hassio/app/chunk.ec48cf4e7f87d6417353.js.LICENSE" } \ No newline at end of file From 9811f1185984032039c449e7b2a5009ba26e06d1 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Wed, 8 Apr 2020 12:34:42 +0000 Subject: [PATCH 17/17] Force plugin: cli v25 - dns v6 --- supervisor/plugins/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supervisor/plugins/__init__.py b/supervisor/plugins/__init__.py index 0c0d91614..79b91338b 100644 --- a/supervisor/plugins/__init__.py +++ b/supervisor/plugins/__init__.py @@ -15,8 +15,8 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) class PluginManager(CoreSysAttributes): """Manage supported function for plugins.""" - required_cli: int = 24 - required_dns: int = 5 + required_cli: int = 25 + required_dns: int = 6 required_audio: int = 14 required_multicast: int = 2