mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-09-07 20:26:22 +00:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
754cd64213 | ||
![]() |
113b62ee77 | ||
![]() |
d9874c4c3e | ||
![]() |
ca44e858c5 | ||
![]() |
c7ca4de307 | ||
![]() |
b77146a4e0 | ||
![]() |
45b4800378 | ||
![]() |
7f9232d2b9 | ||
![]() |
d90426f745 | ||
![]() |
c2deabb672 | ||
![]() |
ead5993f3e | ||
![]() |
1bcd74e8fa | ||
![]() |
118da3c275 | ||
![]() |
d7bb9013d4 | ||
![]() |
812c46d82b | ||
![]() |
c0462b28cd |
4
API.md
4
API.md
@@ -480,6 +480,7 @@ Get all available addons.
|
||||
"build": "bool",
|
||||
"options": "{}",
|
||||
"network": "{}|null",
|
||||
"network_description": "{}|null",
|
||||
"host_network": "bool",
|
||||
"host_pid": "bool",
|
||||
"host_ipc": "bool",
|
||||
@@ -512,7 +513,8 @@ Get all available addons.
|
||||
"ip_address": "ip address",
|
||||
"ingress": "bool",
|
||||
"ingress_entry": "null|/api/hassio_ingress/slug",
|
||||
"ingress_url": "null|/api/hassio_ingress/slug/entry.html"
|
||||
"ingress_url": "null|/api/hassio_ingress/slug/entry.html",
|
||||
"ingress_port": "null|int"
|
||||
}
|
||||
```
|
||||
|
||||
|
@@ -56,6 +56,7 @@ from ..const import (
|
||||
ATTR_NETWORK,
|
||||
ATTR_OPTIONS,
|
||||
ATTR_PORTS,
|
||||
ATTR_PORTS_DESCRIPTION,
|
||||
ATTR_PRIVILEGED,
|
||||
ATTR_PROTECTED,
|
||||
ATTR_REPOSITORY,
|
||||
@@ -322,7 +323,7 @@ class Addon(CoreSysAttributes):
|
||||
return self._mesh[ATTR_REPOSITORY]
|
||||
|
||||
@property
|
||||
def last_version(self):
|
||||
def latest_version(self):
|
||||
"""Return version of add-on."""
|
||||
if self._id in self._data.cache:
|
||||
return self._data.cache[self._id][ATTR_VERSION]
|
||||
@@ -364,10 +365,15 @@ class Addon(CoreSysAttributes):
|
||||
"""Return list of discoverable components/platforms."""
|
||||
return self._mesh.get(ATTR_DISCOVERY, [])
|
||||
|
||||
@property
|
||||
def ports_description(self):
|
||||
"""Return descriptions of ports."""
|
||||
return self._mesh.get(ATTR_PORTS_DESCRIPTION)
|
||||
|
||||
@property
|
||||
def ports(self):
|
||||
"""Return ports of add-on."""
|
||||
if self.host_network or ATTR_PORTS not in self._mesh:
|
||||
if ATTR_PORTS not in self._mesh:
|
||||
return None
|
||||
|
||||
if not self.is_installed or \
|
||||
@@ -380,13 +386,15 @@ class Addon(CoreSysAttributes):
|
||||
"""Set custom ports of add-on."""
|
||||
if value is None:
|
||||
self._data.user[self._id].pop(ATTR_NETWORK, None)
|
||||
else:
|
||||
new_ports = {}
|
||||
for container_port, host_port in value.items():
|
||||
if container_port in self._mesh.get(ATTR_PORTS, {}):
|
||||
new_ports[container_port] = host_port
|
||||
return
|
||||
|
||||
self._data.user[self._id][ATTR_NETWORK] = new_ports
|
||||
# Secure map ports to value
|
||||
new_ports = {}
|
||||
for container_port, host_port in value.items():
|
||||
if container_port in self._mesh.get(ATTR_PORTS, {}):
|
||||
new_ports[container_port] = host_port
|
||||
|
||||
self._data.user[self._id][ATTR_NETWORK] = new_ports
|
||||
|
||||
@property
|
||||
def ingress_url(self):
|
||||
@@ -431,9 +439,15 @@ class Addon(CoreSysAttributes):
|
||||
return f"{proto}://[HOST]:{port}{s_suffix}"
|
||||
|
||||
@property
|
||||
def ingress_internal(self):
|
||||
"""Return Ingress host URL."""
|
||||
return f"http://{self.ip_address}:{self._mesh[ATTR_INGRESS_PORT]}"
|
||||
def ingress_port(self):
|
||||
"""Return Ingress port."""
|
||||
if not self.is_installed or not self.with_ingress:
|
||||
return None
|
||||
|
||||
port = self._mesh[ATTR_INGRESS_PORT]
|
||||
if port == 0:
|
||||
return self.sys_ingress.get_dynamic_port(self.slug)
|
||||
return port
|
||||
|
||||
@property
|
||||
def host_network(self):
|
||||
@@ -845,11 +859,11 @@ class Addon(CoreSysAttributes):
|
||||
await self._install_apparmor()
|
||||
|
||||
try:
|
||||
await self.instance.install(self.last_version, self.image_next)
|
||||
await self.instance.install(self.latest_version, self.image_next)
|
||||
except DockerAPIError:
|
||||
raise AddonsError() from None
|
||||
else:
|
||||
self._set_install(self.image_next, self.last_version)
|
||||
self._set_install(self.image_next, self.latest_version)
|
||||
|
||||
@check_installed
|
||||
async def uninstall(self) -> None:
|
||||
@@ -922,7 +936,7 @@ class Addon(CoreSysAttributes):
|
||||
@check_installed
|
||||
async def update(self) -> None:
|
||||
"""Update add-on."""
|
||||
if self.last_version == self.version_installed:
|
||||
if self.latest_version == self.version_installed:
|
||||
_LOGGER.warning("No update available for add-on %s", self._id)
|
||||
return
|
||||
|
||||
@@ -936,10 +950,10 @@ class Addon(CoreSysAttributes):
|
||||
# Update instance
|
||||
last_state = await self.state()
|
||||
try:
|
||||
await self.instance.update(self.last_version, self.image_next)
|
||||
await self.instance.update(self.latest_version, self.image_next)
|
||||
except DockerAPIError:
|
||||
raise AddonsError() from None
|
||||
self._set_update(self.image_next, self.last_version)
|
||||
self._set_update(self.image_next, self.latest_version)
|
||||
|
||||
# Setup/Fix AppArmor profile
|
||||
await self._install_apparmor()
|
||||
@@ -977,8 +991,11 @@ class Addon(CoreSysAttributes):
|
||||
last_state = await self.state()
|
||||
|
||||
if not self.need_build:
|
||||
_LOGGER.error("Can't rebuild a none local build add-on!")
|
||||
_LOGGER.error("Can't rebuild a image based add-on")
|
||||
raise AddonsNotSupportedError()
|
||||
if self.version_installed != self.latest_version:
|
||||
_LOGGER.error("Version changed, use Update instead Rebuild")
|
||||
raise AddonsError()
|
||||
|
||||
# remove docker container but not addon config
|
||||
try:
|
||||
@@ -986,6 +1003,8 @@ class Addon(CoreSysAttributes):
|
||||
await self.instance.install(self.version_installed)
|
||||
except DockerAPIError:
|
||||
raise AddonsError() from None
|
||||
else:
|
||||
self._set_update(self.image, self.version_installed)
|
||||
|
||||
# restore state
|
||||
if last_state == STATE_STARTED:
|
||||
|
@@ -30,8 +30,8 @@ from ..const import (
|
||||
ATTR_GPIO,
|
||||
ATTR_HASSIO_API,
|
||||
ATTR_HASSIO_ROLE,
|
||||
ATTR_HOMEASSISTANT_API,
|
||||
ATTR_HOMEASSISTANT,
|
||||
ATTR_HOMEASSISTANT_API,
|
||||
ATTR_HOST_DBUS,
|
||||
ATTR_HOST_IPC,
|
||||
ATTR_HOST_NETWORK,
|
||||
@@ -51,6 +51,7 @@ from ..const import (
|
||||
ATTR_NETWORK,
|
||||
ATTR_OPTIONS,
|
||||
ATTR_PORTS,
|
||||
ATTR_PORTS_DESCRIPTION,
|
||||
ATTR_PRIVILEGED,
|
||||
ATTR_PROTECTED,
|
||||
ATTR_REPOSITORY,
|
||||
@@ -81,7 +82,14 @@ from ..const import (
|
||||
STATE_STOPPED,
|
||||
)
|
||||
from ..discovery.validate import valid_discovery_service
|
||||
from ..validate import ALSA_DEVICE, DOCKER_PORTS, NETWORK_PORT, TOKEN, UUID_MATCH
|
||||
from ..validate import (
|
||||
ALSA_DEVICE,
|
||||
DOCKER_PORTS,
|
||||
DOCKER_PORTS_DESCRIPTION,
|
||||
NETWORK_PORT,
|
||||
TOKEN,
|
||||
UUID_MATCH,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -145,10 +153,11 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
|
||||
vol.Required(ATTR_BOOT):
|
||||
vol.In([BOOT_AUTO, BOOT_MANUAL]),
|
||||
vol.Optional(ATTR_PORTS): DOCKER_PORTS,
|
||||
vol.Optional(ATTR_PORTS_DESCRIPTION): DOCKER_PORTS_DESCRIPTION,
|
||||
vol.Optional(ATTR_WEBUI):
|
||||
vol.Match(r"^(?:https?|\[PROTO:\w+\]):\/\/\[HOST\]:\[PORT:\d+\].*$"),
|
||||
vol.Optional(ATTR_INGRESS, default=False): vol.Boolean(),
|
||||
vol.Optional(ATTR_INGRESS_PORT, default=8099): NETWORK_PORT,
|
||||
vol.Optional(ATTR_INGRESS_PORT, default=8099): vol.Any(NETWORK_PORT, vol.Equal(0)),
|
||||
vol.Optional(ATTR_INGRESS_ENTRY): vol.Coerce(str),
|
||||
vol.Optional(ATTR_HOMEASSISTANT): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
||||
|
@@ -44,6 +44,7 @@ from ..const import (
|
||||
ATTR_ICON,
|
||||
ATTR_INGRESS,
|
||||
ATTR_INGRESS_ENTRY,
|
||||
ATTR_INGRESS_PORT,
|
||||
ATTR_INGRESS_URL,
|
||||
ATTR_INSTALLED,
|
||||
ATTR_IP_ADDRESS,
|
||||
@@ -57,6 +58,7 @@ from ..const import (
|
||||
ATTR_MEMORY_USAGE,
|
||||
ATTR_NAME,
|
||||
ATTR_NETWORK,
|
||||
ATTR_NETWORK_DESCRIPTION,
|
||||
ATTR_NETWORK_RX,
|
||||
ATTR_NETWORK_TX,
|
||||
ATTR_OPTIONS,
|
||||
@@ -135,7 +137,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_NAME: addon.name,
|
||||
ATTR_SLUG: addon.slug,
|
||||
ATTR_DESCRIPTON: addon.description,
|
||||
ATTR_VERSION: addon.last_version,
|
||||
ATTR_VERSION: addon.latest_version,
|
||||
ATTR_INSTALLED: addon.version_installed,
|
||||
ATTR_AVAILABLE: addon.available,
|
||||
ATTR_DETACHED: addon.is_detached,
|
||||
@@ -179,7 +181,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_VERSION: addon.version_installed,
|
||||
ATTR_AUTO_UPDATE: addon.auto_update,
|
||||
ATTR_REPOSITORY: addon.repository,
|
||||
ATTR_LAST_VERSION: addon.last_version,
|
||||
ATTR_LAST_VERSION: addon.latest_version,
|
||||
ATTR_STATE: await addon.state(),
|
||||
ATTR_PROTECTED: addon.protected,
|
||||
ATTR_RATING: rating_security(addon),
|
||||
@@ -193,6 +195,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_AVAILABLE: addon.available,
|
||||
ATTR_BUILD: addon.need_build,
|
||||
ATTR_NETWORK: addon.ports,
|
||||
ATTR_NETWORK_DESCRIPTION: addon.ports_description,
|
||||
ATTR_HOST_NETWORK: addon.host_network,
|
||||
ATTR_HOST_PID: addon.host_pid,
|
||||
ATTR_HOST_IPC: addon.host_ipc,
|
||||
@@ -223,6 +226,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_INGRESS: addon.with_ingress,
|
||||
ATTR_INGRESS_ENTRY: addon.ingress_entry,
|
||||
ATTR_INGRESS_URL: addon.ingress_url,
|
||||
ATTR_INGRESS_PORT: addon.ingress_port,
|
||||
}
|
||||
|
||||
@api_process
|
||||
@@ -315,7 +319,7 @@ class APIAddons(CoreSysAttributes):
|
||||
"""Update add-on."""
|
||||
addon = self._extract_addon(request)
|
||||
|
||||
if addon.last_version == addon.version_installed:
|
||||
if addon.latest_version == addon.version_installed:
|
||||
raise APIError("No update available!")
|
||||
|
||||
return asyncio.shield(addon.update())
|
||||
|
@@ -63,7 +63,7 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
"""Return host information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_homeassistant.version,
|
||||
ATTR_LAST_VERSION: self.sys_homeassistant.last_version,
|
||||
ATTR_LAST_VERSION: self.sys_homeassistant.latest_version,
|
||||
ATTR_MACHINE: self.sys_homeassistant.machine,
|
||||
ATTR_IP_ADDRESS: str(self.sys_homeassistant.ip_address),
|
||||
ATTR_ARCH: self.sys_homeassistant.arch,
|
||||
@@ -83,7 +83,7 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
|
||||
if ATTR_IMAGE in body and ATTR_LAST_VERSION in body:
|
||||
self.sys_homeassistant.image = body[ATTR_IMAGE]
|
||||
self.sys_homeassistant.last_version = body[ATTR_LAST_VERSION]
|
||||
self.sys_homeassistant.latest_version = body[ATTR_LAST_VERSION]
|
||||
|
||||
if ATTR_BOOT in body:
|
||||
self.sys_homeassistant.boot = body[ATTR_BOOT]
|
||||
@@ -130,7 +130,7 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
async def update(self, request: web.Request) -> None:
|
||||
"""Update Home Assistant."""
|
||||
body = await api_validate(SCHEMA_VERSION, request)
|
||||
version = body.get(ATTR_VERSION, self.sys_homeassistant.last_version)
|
||||
version = body.get(ATTR_VERSION, self.sys_homeassistant.latest_version)
|
||||
|
||||
await asyncio.shield(self.sys_homeassistant.update(version))
|
||||
|
||||
|
@@ -43,7 +43,7 @@ class APIIngress(CoreSysAttributes):
|
||||
|
||||
def _create_url(self, addon: Addon, path: str) -> str:
|
||||
"""Create URL to container."""
|
||||
return f"{addon.ingress_internal}/{path}"
|
||||
return f"http://{addon.ip_address}:{addon.ingress_port}/{path}"
|
||||
|
||||
@api_process
|
||||
async def create_session(self, request: web.Request) -> Dict[str, Any]:
|
||||
@@ -85,7 +85,17 @@ class APIIngress(CoreSysAttributes):
|
||||
self, request: web.Request, addon: Addon, path: str
|
||||
) -> web.WebSocketResponse:
|
||||
"""Ingress route for websocket."""
|
||||
ws_server = web.WebSocketResponse()
|
||||
if hdrs.SEC_WEBSOCKET_PROTOCOL in request.headers:
|
||||
req_protocols = [
|
||||
str(proto.strip())
|
||||
for proto in request.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
|
||||
]
|
||||
else:
|
||||
req_protocols = ()
|
||||
|
||||
ws_server = web.WebSocketResponse(
|
||||
protocols=req_protocols, autoclose=False, autoping=False
|
||||
)
|
||||
await ws_server.prepare(request)
|
||||
|
||||
# Preparing
|
||||
@@ -98,7 +108,11 @@ class APIIngress(CoreSysAttributes):
|
||||
|
||||
# Start proxy
|
||||
async with self.sys_websession.ws_connect(
|
||||
url, headers=source_header
|
||||
url,
|
||||
headers=source_header,
|
||||
protocols=req_protocols,
|
||||
autoclose=False,
|
||||
autoping=False,
|
||||
) as ws_client:
|
||||
# Proxy requests
|
||||
await asyncio.wait(
|
||||
@@ -131,7 +145,12 @@ class APIIngress(CoreSysAttributes):
|
||||
):
|
||||
# Return Response
|
||||
body = await result.read()
|
||||
return web.Response(headers=headers, status=result.status, body=body)
|
||||
return web.Response(
|
||||
headers=headers,
|
||||
status=result.status,
|
||||
content_type=result.content_type,
|
||||
body=body,
|
||||
)
|
||||
|
||||
# Stream response
|
||||
response = web.StreamResponse(status=result.status, headers=headers)
|
||||
@@ -156,12 +175,7 @@ def _init_header(
|
||||
|
||||
# filter flags
|
||||
for name, value in request.headers.items():
|
||||
if name in (
|
||||
hdrs.CONTENT_LENGTH,
|
||||
hdrs.CONTENT_TYPE,
|
||||
hdrs.CONTENT_ENCODING,
|
||||
istr(HEADER_TOKEN),
|
||||
):
|
||||
if name in (hdrs.CONTENT_LENGTH, hdrs.CONTENT_ENCODING, istr(HEADER_TOKEN)):
|
||||
continue
|
||||
headers[name] = value
|
||||
|
||||
@@ -204,14 +218,17 @@ def _is_websocket(request: web.Request) -> bool:
|
||||
|
||||
async def _websocket_forward(ws_from, ws_to):
|
||||
"""Handle websocket message directly."""
|
||||
async for msg in ws_from:
|
||||
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||
await ws_to.send_str(msg.data)
|
||||
elif msg.type == aiohttp.WSMsgType.BINARY:
|
||||
await ws_to.send_bytes(msg.data)
|
||||
elif msg.type == aiohttp.WSMsgType.PING:
|
||||
await ws_to.ping()
|
||||
elif msg.type == aiohttp.WSMsgType.PONG:
|
||||
await ws_to.pong()
|
||||
elif ws_to.closed:
|
||||
await ws_to.close(code=ws_to.close_code, message=msg.extra)
|
||||
try:
|
||||
async for msg in ws_from:
|
||||
if msg.type == aiohttp.WSMsgType.TEXT:
|
||||
await ws_to.send_str(msg.data)
|
||||
elif msg.type == aiohttp.WSMsgType.BINARY:
|
||||
await ws_to.send_bytes(msg.data)
|
||||
elif msg.type == aiohttp.WSMsgType.PING:
|
||||
await ws_to.ping()
|
||||
elif msg.type == aiohttp.WSMsgType.PONG:
|
||||
await ws_to.pong()
|
||||
elif ws_to.closed:
|
||||
await ws_to.close(code=ws_to.close_code, message=msg.extra)
|
||||
except RuntimeError:
|
||||
_LOGGER.warning("Ingress Websocket runtime error")
|
||||
|
1
hassio/api/panel/chunk.183d719f6ced86001f8e.js
Normal file
1
hassio/api/panel/chunk.183d719f6ced86001f8e.js
Normal file
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.183d719f6ced86001f8e.js.gz
Normal file
BIN
hassio/api/panel/chunk.183d719f6ced86001f8e.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
1
hassio/api/panel/chunk.3a63ad36bccf4ea567fa.js
Normal file
1
hassio/api/panel/chunk.3a63ad36bccf4ea567fa.js
Normal file
@@ -0,0 +1 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{102:function(n,r,t){"use strict";t.r(r),t.d(r,"marked",function(){return a}),t.d(r,"filterXSS",function(){return c});var e=t(121),i=t.n(e),o=t(123),u=t.n(o),a=i.a,c=u.a}}]);
|
BIN
hassio/api/panel/chunk.3a63ad36bccf4ea567fa.js.gz
Normal file
BIN
hassio/api/panel/chunk.3a63ad36bccf4ea567fa.js.gz
Normal file
Binary file not shown.
3
hassio/api/panel/chunk.510634470d399e194ace.js
Normal file
3
hassio/api/panel/chunk.510634470d399e194ace.js
Normal file
File diff suppressed because one or more lines are too long
10
hassio/api/panel/chunk.510634470d399e194ace.js.LICENSE
Normal file
10
hassio/api/panel/chunk.510634470d399e194ace.js.LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
hassio/api/panel/chunk.510634470d399e194ace.js.gz
Normal file
BIN
hassio/api/panel/chunk.510634470d399e194ace.js.gz
Normal file
Binary file not shown.
@@ -1 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.31b41b04602ce627ad98.js","sourceRoot":""}
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.510634470d399e194ace.js","sourceRoot":""}
|
3
hassio/api/panel/chunk.564a2f7b1c38ddaa4ce0.js
Normal file
3
hassio/api/panel/chunk.564a2f7b1c38ddaa4ce0.js
Normal file
File diff suppressed because one or more lines are too long
21
hassio/api/panel/chunk.564a2f7b1c38ddaa4ce0.js.LICENSE
Normal file
21
hassio/api/panel/chunk.564a2f7b1c38ddaa4ce0.js.LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
hassio/api/panel/chunk.564a2f7b1c38ddaa4ce0.js.gz
Normal file
BIN
hassio/api/panel/chunk.564a2f7b1c38ddaa4ce0.js.gz
Normal file
Binary file not shown.
@@ -1 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.1ac383635811d6c2cb4b.js","sourceRoot":""}
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.564a2f7b1c38ddaa4ce0.js","sourceRoot":""}
|
1
hassio/api/panel/chunk.5d31a1778f717ac8b063.js
Normal file
1
hassio/api/panel/chunk.5d31a1778f717ac8b063.js
Normal file
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.5d31a1778f717ac8b063.js.gz
Normal file
BIN
hassio/api/panel/chunk.5d31a1778f717ac8b063.js.gz
Normal file
Binary file not shown.
3
hassio/api/panel/chunk.659084fef4e3b7b66a76.js
Normal file
3
hassio/api/panel/chunk.659084fef4e3b7b66a76.js
Normal file
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.659084fef4e3b7b66a76.js.gz
Normal file
BIN
hassio/api/panel/chunk.659084fef4e3b7b66a76.js.gz
Normal file
Binary file not shown.
1
hassio/api/panel/chunk.659084fef4e3b7b66a76.js.map
Normal file
1
hassio/api/panel/chunk.659084fef4e3b7b66a76.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.659084fef4e3b7b66a76.js","sourceRoot":""}
|
3
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js
Normal file
3
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js
Normal file
File diff suppressed because one or more lines are too long
31
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js.LICENSE
Normal file
31
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js.LICENSE
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js.gz
Normal file
BIN
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js.gz
Normal file
Binary file not shown.
1
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js.map
Normal file
1
hassio/api/panel/chunk.6e9c87e51920a9c354e5.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.6e9c87e51920a9c354e5.js","sourceRoot":""}
|
1
hassio/api/panel/chunk.739b67c99ab56cdbd75d.js
Normal file
1
hassio/api/panel/chunk.739b67c99ab56cdbd75d.js
Normal file
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.739b67c99ab56cdbd75d.js.gz
Normal file
BIN
hassio/api/panel/chunk.739b67c99ab56cdbd75d.js.gz
Normal file
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{114:function(n,r,t){"use strict";t.r(r),t.d(r,"marked",function(){return a}),t.d(r,"filterXSS",function(){return c});var e=t(104),i=t.n(e),o=t(106),u=t.n(o),a=i.a,c=u.a}}]);
|
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.a571dfa106202cc57af6.js.gz
Normal file
BIN
hassio/api/panel/chunk.a571dfa106202cc57af6.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
3
hassio/api/panel/chunk.a7e5fb452cd1b3a5faef.js
Normal file
3
hassio/api/panel/chunk.a7e5fb452cd1b3a5faef.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,3 +1,28 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at
|
||||
* http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at
|
||||
* http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at
|
||||
* http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at
|
||||
* http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
@@ -30,20 +55,6 @@ part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at
|
||||
* http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at
|
||||
* http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at
|
||||
* http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at
|
||||
* http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright 2018 Google Inc. All Rights Reserved.
|
||||
@@ -122,17 +133,6 @@ and limitations under the License.
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2019 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
|
||||
@@ -147,17 +147,6 @@ found at http://polymer.github.io/PATENTS.txt
|
||||
* http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
||||
@@ -178,3 +167,14 @@ The complete set of contributors may be found at http://polymer.github.io/CONTRI
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
hassio/api/panel/chunk.a7e5fb452cd1b3a5faef.js.gz
Normal file
BIN
hassio/api/panel/chunk.a7e5fb452cd1b3a5faef.js.gz
Normal file
Binary file not shown.
1
hassio/api/panel/chunk.a7e5fb452cd1b3a5faef.js.map
Normal file
1
hassio/api/panel/chunk.a7e5fb452cd1b3a5faef.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.a7e5fb452cd1b3a5faef.js","sourceRoot":""}
|
1
hassio/api/panel/chunk.b3340b3df270d20af4a1.js
Normal file
1
hassio/api/panel/chunk.b3340b3df270d20af4a1.js
Normal file
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.b3340b3df270d20af4a1.js.gz
Normal file
BIN
hassio/api/panel/chunk.b3340b3df270d20af4a1.js.gz
Normal file
Binary file not shown.
3
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js
Normal file
3
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js
Normal file
File diff suppressed because one or more lines are too long
10
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js.LICENSE
Normal file
10
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js.LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found at
|
||||
http://polymer.github.io/AUTHORS.txt The complete set of contributors may be
|
||||
found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as
|
||||
part of the polymer project is also subject to an additional IP rights grant
|
||||
found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
BIN
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js.gz
Normal file
BIN
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js.gz
Normal file
Binary file not shown.
1
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js.map
Normal file
1
hassio/api/panel/chunk.f15d7f41c0d302cbbc7a.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk.f15d7f41c0d302cbbc7a.js","sourceRoot":""}
|
1
hassio/api/panel/chunk.feb68d852b3f6d67d970.js
Normal file
1
hassio/api/panel/chunk.feb68d852b3f6d67d970.js
Normal file
File diff suppressed because one or more lines are too long
BIN
hassio/api/panel/chunk.feb68d852b3f6d67d970.js.gz
Normal file
BIN
hassio/api/panel/chunk.feb68d852b3f6d67d970.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +1 @@
|
||||
!function(e){function n(n){for(var t,o,i=n[0],a=n[1],u=0,f=[];u<i.length;u++)o=i[u],r[o]&&f.push(r[o][0]),r[o]=0;for(t in a)Object.prototype.hasOwnProperty.call(a,t)&&(e[t]=a[t]);for(c&&c(n);f.length;)f.shift()()}var t={},r={1:0};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.e=function(e){var n=[],t=r[e];if(0!==t)if(t)n.push(t[2]);else{var i=new Promise(function(n,o){t=r[e]=[n,o]});n.push(t[2]=i);var a,u=document.createElement("script");u.charset="utf-8",u.timeout=120,o.nc&&u.setAttribute("nonce",o.nc),u.src=function(e){return o.p+"chunk."+{0:"1ac383635811d6c2cb4b",2:"381b1e7d41316cfb583c",3:"a6e3bc73416702354e6d",4:"8a4a3a3274af0f09d86b",5:"7589a9f39a552ee63688",6:"31b41b04602ce627ad98",7:"ff45557361d5d6bd46af"}[e]+".js"}(e),a=function(n){u.onerror=u.onload=null,clearTimeout(c);var t=r[e];if(0!==t){if(t){var o=n&&("load"===n.type?"missing":n.type),i=n&&n.target&&n.target.src,a=new Error("Loading chunk "+e+" failed.\n("+o+": "+i+")");a.type=o,a.request=i,t[1](a)}r[e]=void 0}};var c=setTimeout(function(){a({type:"timeout",target:u})},12e4);u.onerror=u.onload=a,document.head.appendChild(u)}return Promise.all(n)},o.m=e,o.c=t,o.d=function(e,n,t){o.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,n){if(1&n&&(e=o(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(o.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)o.d(t,r,function(n){return e[n]}.bind(null,r));return t},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/api/hassio/app/",o.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],a=i.push.bind(i);i.push=n,i=i.slice();for(var u=0;u<i.length;u++)n(i[u]);var c=a;o(o.s=0)}([function(e,n,t){window.loadES5Adapter().then(function(){Promise.all([t.e(0),t.e(2)]).then(t.bind(null,2)),Promise.all([t.e(0),t.e(6),t.e(3)]).then(t.bind(null,1))});var r=document.createElement("style");r.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",document.head.appendChild(r)}]);
|
||||
!function(e){function n(n){for(var t,o,a=n[0],i=n[1],c=0,u=[];c<a.length;c++)o=a[c],r[o]&&u.push(r[o][0]),r[o]=0;for(t in i)Object.prototype.hasOwnProperty.call(i,t)&&(e[t]=i[t]);for(f&&f(n);u.length;)u.shift()()}var t={},r={4:0};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.e=function(e){var n=[],t=r[e];if(0!==t)if(t)n.push(t[2]);else{var a=new Promise(function(n,o){t=r[e]=[n,o]});n.push(t[2]=a);var i,c=document.createElement("script");c.charset="utf-8",c.timeout=120,o.nc&&c.setAttribute("nonce",o.nc),c.src=function(e){return o.p+"chunk."+{0:"564a2f7b1c38ddaa4ce0",1:"659084fef4e3b7b66a76",2:"510634470d399e194ace",3:"f15d7f41c0d302cbbc7a",5:"5d31a1778f717ac8b063",6:"feb68d852b3f6d67d970",7:"3a63ad36bccf4ea567fa",8:"a571dfa106202cc57af6",9:"a7e5fb452cd1b3a5faef",10:"b3340b3df270d20af4a1",11:"6e9c87e51920a9c354e5",12:"183d719f6ced86001f8e",13:"739b67c99ab56cdbd75d"}[e]+".js"}(e),i=function(n){c.onerror=c.onload=null,clearTimeout(f);var t=r[e];if(0!==t){if(t){var o=n&&("load"===n.type?"missing":n.type),a=n&&n.target&&n.target.src,i=new Error("Loading chunk "+e+" failed.\n("+o+": "+a+")");i.type=o,i.request=a,t[1](i)}r[e]=void 0}};var f=setTimeout(function(){i({type:"timeout",target:c})},12e4);c.onerror=c.onload=i,document.head.appendChild(c)}return Promise.all(n)},o.m=e,o.c=t,o.d=function(e,n,t){o.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,n){if(1&n&&(e=o(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(o.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)o.d(t,r,function(n){return e[n]}.bind(null,r));return t},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="/api/hassio/app/",o.oe=function(e){throw console.error(e),e};var a=window.webpackJsonp=window.webpackJsonp||[],i=a.push.bind(a);a.push=n,a=a.slice();for(var c=0;c<a.length;c++)n(a[c]);var f=i;o(o.s=0)}([function(e,n,t){window.loadES5Adapter().then(function(){Promise.all([t.e(1),t.e(5)]).then(t.bind(null,2)),Promise.all([t.e(1),t.e(9),t.e(6)]).then(t.bind(null,1))});var r=document.createElement("style");r.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",document.head.appendChild(r)}]);
|
Binary file not shown.
@@ -74,7 +74,7 @@ class APISupervisor(CoreSysAttributes):
|
||||
ATTR_SLUG: addon.slug,
|
||||
ATTR_DESCRIPTON: addon.description,
|
||||
ATTR_STATE: await addon.state(),
|
||||
ATTR_VERSION: addon.last_version,
|
||||
ATTR_VERSION: addon.latest_version,
|
||||
ATTR_INSTALLED: addon.version_installed,
|
||||
ATTR_REPOSITORY: addon.repository,
|
||||
ATTR_ICON: addon.with_icon,
|
||||
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
||||
from ipaddress import ip_network
|
||||
|
||||
|
||||
HASSIO_VERSION = "153"
|
||||
HASSIO_VERSION = "156"
|
||||
|
||||
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
||||
URL_HASSIO_VERSION = "https://s3.amazonaws.com/hassio-version/{channel}.json"
|
||||
@@ -92,6 +92,7 @@ ATTR_DESCRIPTON = "description"
|
||||
ATTR_STARTUP = "startup"
|
||||
ATTR_BOOT = "boot"
|
||||
ATTR_PORTS = "ports"
|
||||
ATTR_PORTS_DESCRIPTION = "ports_description"
|
||||
ATTR_PORT = "port"
|
||||
ATTR_SSL = "ssl"
|
||||
ATTR_MAP = "map"
|
||||
@@ -122,6 +123,7 @@ ATTR_HOST_PID = "host_pid"
|
||||
ATTR_HOST_IPC = "host_ipc"
|
||||
ATTR_HOST_DBUS = "host_dbus"
|
||||
ATTR_NETWORK = "network"
|
||||
ATTR_NETWORK_DESCRIPTION = "network_description"
|
||||
ATTR_TMPFS = "tmpfs"
|
||||
ATTR_PRIVILEGED = "privileged"
|
||||
ATTR_USER = "user"
|
||||
|
@@ -147,7 +147,7 @@ class DockerAddon(DockerInterface):
|
||||
@property
|
||||
def ports(self) -> Optional[Dict[str, Union[str, int, None]]]:
|
||||
"""Filter None from add-on ports."""
|
||||
if not self.addon.ports:
|
||||
if self.addon.host_network or not self.addon.ports:
|
||||
return None
|
||||
|
||||
return {
|
||||
@@ -355,8 +355,8 @@ class DockerAddon(DockerInterface):
|
||||
"""
|
||||
if self.addon.need_build:
|
||||
self._build(tag)
|
||||
|
||||
super()._install(tag, image)
|
||||
else:
|
||||
super()._install(tag, image)
|
||||
|
||||
def _build(self, tag: str) -> None:
|
||||
"""Build a Docker container.
|
||||
|
@@ -164,14 +164,14 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
return self.instance.version
|
||||
|
||||
@property
|
||||
def last_version(self) -> str:
|
||||
def latest_version(self) -> str:
|
||||
"""Return last available version of Home Assistant."""
|
||||
if self.is_custom_image:
|
||||
return self._data.get(ATTR_LAST_VERSION)
|
||||
return self.sys_updater.version_homeassistant
|
||||
|
||||
@last_version.setter
|
||||
def last_version(self, value: str):
|
||||
@latest_version.setter
|
||||
def latest_version(self, value: str):
|
||||
"""Set last available version of Home Assistant."""
|
||||
if value:
|
||||
self._data[ATTR_LAST_VERSION] = value
|
||||
@@ -246,10 +246,10 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
_LOGGER.info("Setup Home Assistant")
|
||||
while True:
|
||||
# read homeassistant tag and install it
|
||||
if not self.last_version:
|
||||
if not self.latest_version:
|
||||
await self.sys_updater.reload()
|
||||
|
||||
tag = self.last_version
|
||||
tag = self.latest_version
|
||||
if tag:
|
||||
with suppress(DockerAPIError):
|
||||
await self.instance.install(tag)
|
||||
@@ -273,7 +273,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
||||
@process_lock
|
||||
async def update(self, version=None) -> None:
|
||||
"""Update HomeAssistant version."""
|
||||
version = version or self.last_version
|
||||
version = version or self.latest_version
|
||||
rollback = self.version if not self.error_state else None
|
||||
running = await self.instance.is_running()
|
||||
exists = await self.instance.exists()
|
||||
|
@@ -1,14 +1,15 @@
|
||||
"""Fetch last versions from webserver."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Dict, Optional
|
||||
import random
|
||||
import secrets
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .addons.addon import Addon
|
||||
from .const import ATTR_SESSION, FILE_HASSIO_INGRESS
|
||||
from .const import ATTR_PORTS, ATTR_SESSION, FILE_HASSIO_INGRESS
|
||||
from .coresys import CoreSys, CoreSysAttributes
|
||||
from .utils.dt import utc_from_timestamp, utcnow
|
||||
from .utils.json import JsonConfig
|
||||
from .utils.dt import utcnow, utc_from_timestamp
|
||||
from .validate import SCHEMA_INGRESS_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -34,6 +35,11 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
||||
"""Return sessions."""
|
||||
return self._data[ATTR_SESSION]
|
||||
|
||||
@property
|
||||
def ports(self) -> Dict[str, int]:
|
||||
"""Return list of dynamic ports."""
|
||||
return self._data[ATTR_PORTS]
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Update internal data."""
|
||||
self._update_token_list()
|
||||
@@ -101,3 +107,14 @@ class Ingress(JsonConfig, CoreSysAttributes):
|
||||
self.sessions[session] = valid_until.timestamp()
|
||||
|
||||
return True
|
||||
|
||||
def get_dynamic_port(self, addon_slug: str) -> int:
|
||||
"""Get/Create a dynamic port from range."""
|
||||
if addon_slug in self.ports:
|
||||
return self.ports[addon_slug]
|
||||
port = random.randint(62000, 65500)
|
||||
|
||||
# Save port for next time
|
||||
self.ports[addon_slug] = port
|
||||
self.save_data()
|
||||
return port
|
||||
|
@@ -417,7 +417,7 @@ class Snapshot(CoreSysAttributes):
|
||||
if self.sys_homeassistant.is_custom_image:
|
||||
self.homeassistant[ATTR_IMAGE] = self.sys_homeassistant.image
|
||||
self.homeassistant[ATTR_LAST_VERSION] = \
|
||||
self.sys_homeassistant.last_version
|
||||
self.sys_homeassistant.latest_version
|
||||
|
||||
# API/Proxy
|
||||
self.homeassistant[ATTR_PORT] = self.sys_homeassistant.api_port
|
||||
@@ -436,7 +436,7 @@ class Snapshot(CoreSysAttributes):
|
||||
# Custom image
|
||||
if self.homeassistant.get(ATTR_IMAGE):
|
||||
self.sys_homeassistant.image = self.homeassistant[ATTR_IMAGE]
|
||||
self.sys_homeassistant.last_version = \
|
||||
self.sys_homeassistant.latest_version = \
|
||||
self.homeassistant[ATTR_LAST_VERSION]
|
||||
|
||||
# API/Proxy
|
||||
|
@@ -49,7 +49,7 @@ class Supervisor(CoreSysAttributes):
|
||||
@property
|
||||
def need_update(self) -> bool:
|
||||
"""Return True if an update is available."""
|
||||
return self.version != self.last_version
|
||||
return self.version != self.latest_version
|
||||
|
||||
@property
|
||||
def version(self) -> str:
|
||||
@@ -57,7 +57,7 @@ class Supervisor(CoreSysAttributes):
|
||||
return self.instance.version
|
||||
|
||||
@property
|
||||
def last_version(self) -> str:
|
||||
def latest_version(self) -> str:
|
||||
"""Return last available version of Home Assistant."""
|
||||
return self.sys_updater.version_hassio
|
||||
|
||||
@@ -101,7 +101,7 @@ class Supervisor(CoreSysAttributes):
|
||||
|
||||
async def update(self, version: Optional[str] = None) -> None:
|
||||
"""Update Home Assistant version."""
|
||||
version = version or self.last_version
|
||||
version = version or self.latest_version
|
||||
|
||||
if version == self.sys_supervisor.version:
|
||||
_LOGGER.warning("Version %s is already installed", version)
|
||||
|
@@ -93,7 +93,7 @@ class Tasks(CoreSysAttributes):
|
||||
if not addon.is_installed or not addon.auto_update:
|
||||
continue
|
||||
|
||||
if addon.version_installed == addon.last_version:
|
||||
if addon.version_installed == addon.latest_version:
|
||||
continue
|
||||
|
||||
if addon.test_update_schema():
|
||||
|
@@ -18,6 +18,7 @@ from .const import (
|
||||
ATTR_LAST_VERSION,
|
||||
ATTR_PASSWORD,
|
||||
ATTR_PORT,
|
||||
ATTR_PORTS,
|
||||
ATTR_REFRESH_TOKEN,
|
||||
ATTR_SESSION,
|
||||
ATTR_SSL,
|
||||
@@ -60,36 +61,18 @@ def validate_repository(repository):
|
||||
REPOSITORIES = vol.All([validate_repository], vol.Unique())
|
||||
|
||||
|
||||
# pylint: disable=inconsistent-return-statements
|
||||
def convert_to_docker_ports(data):
|
||||
"""Convert data into Docker port list."""
|
||||
# dynamic ports
|
||||
if data is None:
|
||||
return None
|
||||
|
||||
# single port
|
||||
if isinstance(data, int):
|
||||
return NETWORK_PORT(data)
|
||||
|
||||
# port list
|
||||
if isinstance(data, list) and len(data) > 2:
|
||||
return vol.Schema([NETWORK_PORT])(data)
|
||||
|
||||
# ip port mapping
|
||||
if isinstance(data, list) and len(data) == 2:
|
||||
return (vol.Coerce(str)(data[0]), NETWORK_PORT(data[1]))
|
||||
|
||||
raise vol.Invalid("Can't validate Docker host settings")
|
||||
|
||||
|
||||
DOCKER_PORTS = vol.Schema(
|
||||
{
|
||||
vol.All(
|
||||
vol.Coerce(str), vol.Match(r"^\d+(?:/tcp|/udp)?$")
|
||||
): convert_to_docker_ports
|
||||
vol.All(vol.Coerce(str), vol.Match(r"^\d+(?:/tcp|/udp)?$")): vol.Maybe(
|
||||
NETWORK_PORT
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
DOCKER_PORTS_DESCRIPTION = vol.Schema(
|
||||
{vol.All(vol.Coerce(str), vol.Match(r"^\d+(?:/tcp|/udp)?$")): vol.Coerce(str)}
|
||||
)
|
||||
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
SCHEMA_HASS_CONFIG = vol.Schema(
|
||||
@@ -143,6 +126,13 @@ SCHEMA_AUTH_CONFIG = vol.Schema({SHA256: SHA256})
|
||||
|
||||
|
||||
SCHEMA_INGRESS_CONFIG = vol.Schema(
|
||||
{vol.Required(ATTR_SESSION, default=dict): vol.Schema({TOKEN: vol.Coerce(float)})},
|
||||
{
|
||||
vol.Required(ATTR_SESSION, default=dict): vol.Schema(
|
||||
{TOKEN: vol.Coerce(float)}
|
||||
),
|
||||
vol.Required(ATTR_PORTS, default=dict): vol.Schema(
|
||||
{vol.Coerce(str): NETWORK_PORT}
|
||||
),
|
||||
},
|
||||
extra=vol.REMOVE_EXTRA,
|
||||
)
|
||||
|
@@ -20,3 +20,22 @@ def test_session_handling(coresys):
|
||||
coresys.ingress.sessions[session] = not_valid.timestamp()
|
||||
assert not coresys.ingress.validate_session(session)
|
||||
assert not coresys.ingress.validate_session("invalid session")
|
||||
|
||||
|
||||
def test_dynamic_ports(coresys):
|
||||
"""Test dyanmic port handling."""
|
||||
port_test1 = coresys.ingress.get_dynamic_port("test1")
|
||||
|
||||
assert port_test1
|
||||
assert coresys.ingress.save_data.called
|
||||
assert port_test1 == coresys.ingress.get_dynamic_port("test1")
|
||||
|
||||
port_test2 = coresys.ingress.get_dynamic_port("test2")
|
||||
|
||||
assert port_test2
|
||||
assert port_test2 != port_test1
|
||||
|
||||
assert port_test2 > 62000
|
||||
assert port_test2 < 65500
|
||||
assert port_test1 > 62000
|
||||
assert port_test1 < 65500
|
||||
|
Reference in New Issue
Block a user