Merge branch 'main' of https://github.com/home-assistant/supervisor into dark_icon-suport

This commit is contained in:
Ludeeus 2022-06-24 09:13:44 +00:00
commit 08af296c86
117 changed files with 298 additions and 407 deletions

View File

@ -128,7 +128,7 @@ jobs:
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
- name: Build supervisor - name: Build supervisor
uses: home-assistant/builder@2022.03.1 uses: home-assistant/builder@2022.06.2
with: with:
args: | args: |
$BUILD_ARGS \ $BUILD_ARGS \
@ -213,7 +213,7 @@ jobs:
- name: Build the Supervisor - name: Build the Supervisor
if: needs.init.outputs.publish != 'true' if: needs.init.outputs.publish != 'true'
uses: home-assistant/builder@2022.03.1 uses: home-assistant/builder@2022.06.2
with: with:
args: | args: |
--test \ --test \

@ -1 +1 @@
Subproject commit 4ad49ef07f65431d047094dda7eb6359fe4d8809 Subproject commit e7848262ea94af35a5333ce704385da511701240

View File

@ -12,24 +12,19 @@ extension-pkg-whitelist=
# locally-disabled - it spams too much # locally-disabled - it spams too much
# duplicate-code - unavoidable # duplicate-code - unavoidable
# cyclic-import - doesn't test if both import on load # cyclic-import - doesn't test if both import on load
# abstract-class-little-used - prevents from setting right foundation
# abstract-class-not-used - is flaky, should not show up but does # abstract-class-not-used - is flaky, should not show up but does
# unused-argument - generic callbacks and setup methods create a lot of warnings # unused-argument - generic callbacks and setup methods create a lot of warnings
# redefined-variable-type - this is Python, we're duck typing!
# too-many-* - are not enforced for the sake of readability # too-many-* - are not enforced for the sake of readability
# too-few-* - same as too-many-* # too-few-* - same as too-many-*
# abstract-method - with intro of async there are always methods missing # abstract-method - with intro of async there are always methods missing
disable= disable=
format, format,
abstract-class-little-used,
abstract-method, abstract-method,
cyclic-import, cyclic-import,
duplicate-code, duplicate-code,
locally-disabled, locally-disabled,
no-else-return, no-else-return,
no-self-use,
not-context-manager, not-context-manager,
redefined-variable-type,
too-few-public-methods, too-few-public-methods,
too-many-arguments, too-many-arguments,
too-many-branches, too-many-branches,

View File

@ -5,11 +5,11 @@ flake8-docstrings==1.6.0
flake8==4.0.1 flake8==4.0.1
pre-commit==2.19.0 pre-commit==2.19.0
pydocstyle==6.1.1 pydocstyle==6.1.1
pylint==2.13.9 pylint==2.14.3
pytest-aiohttp==0.3.0 pytest-aiohttp==0.3.0
pytest-asyncio==0.12.0 # NB!: Versions over 0.12.0 breaks pytest-aiohttp (https://github.com/aio-libs/pytest-aiohttp/issues/16) pytest-asyncio==0.12.0 # NB!: Versions over 0.12.0 breaks pytest-aiohttp (https://github.com/aio-libs/pytest-aiohttp/issues/16)
pytest-cov==3.0.0 pytest-cov==3.0.0
pytest-timeout==2.1.0 pytest-timeout==2.1.0
pytest==7.1.2 pytest==7.1.2
pyupgrade==2.32.1 pyupgrade==2.34.0
time-machine==2.7.0 time-machine==2.7.0

View File

@ -84,8 +84,6 @@ RE_WATCHDOG = re.compile(
r":\/\/\[HOST\]:(?:\[PORT:)?(?P<t_port>\d+)\]?(?P<s_suffix>.*)$" r":\/\/\[HOST\]:(?:\[PORT:)?(?P<t_port>\d+)\]?(?P<s_suffix>.*)$"
) )
RE_OLD_AUDIO = re.compile(r"\d+,\d+")
WATCHDOG_TIMEOUT = aiohttp.ClientTimeout(total=10) WATCHDOG_TIMEOUT = aiohttp.ClientTimeout(total=10)
_OPTIONS_MERGER: Final = Merger( _OPTIONS_MERGER: Final = Merger(
@ -367,13 +365,7 @@ class Addon(AddonModel):
"""Return a pulse profile for output or None.""" """Return a pulse profile for output or None."""
if not self.with_audio: if not self.with_audio:
return None return None
return self.persist.get(ATTR_AUDIO_OUTPUT)
# Fallback with old audio settings
# Remove after 210
output_data = self.persist.get(ATTR_AUDIO_OUTPUT)
if output_data and RE_OLD_AUDIO.fullmatch(output_data):
return None
return output_data
@audio_output.setter @audio_output.setter
def audio_output(self, value: Optional[str]): def audio_output(self, value: Optional[str]):
@ -386,12 +378,7 @@ class Addon(AddonModel):
if not self.with_audio: if not self.with_audio:
return None return None
# Fallback with old audio settings return self.persist.get(ATTR_AUDIO_INPUT)
# Remove after 210
input_data = self.persist.get(ATTR_AUDIO_INPUT)
if input_data and RE_OLD_AUDIO.fullmatch(input_data):
return None
return input_data
@audio_input.setter @audio_input.setter
def audio_input(self, value: Optional[str]) -> None: def audio_input(self, value: Optional[str]) -> None:

View File

@ -240,7 +240,7 @@ class RestAPI(CoreSysAttributes):
[web.get("/available_updates", api_root.available_updates)] [web.get("/available_updates", api_root.available_updates)]
) )
# Remove 2023 # Remove: 2023
self.webapp.add_routes( self.webapp.add_routes(
[web.get("/supervisor/available_updates", api_root.available_updates)] [web.get("/supervisor/available_updates", api_root.available_updates)]
) )
@ -323,17 +323,22 @@ class RestAPI(CoreSysAttributes):
web.post("/core/start", api_hass.start), web.post("/core/start", api_hass.start),
web.post("/core/check", api_hass.check), web.post("/core/check", api_hass.check),
web.post("/core/rebuild", api_hass.rebuild), web.post("/core/rebuild", api_hass.rebuild),
# Remove with old Supervisor fallback ]
)
# Reroute from legacy
self.webapp.add_routes(
[
web.get("/homeassistant/info", api_hass.info), web.get("/homeassistant/info", api_hass.info),
web.get("/homeassistant/logs", api_hass.logs), web.get("/homeassistant/logs", api_hass.logs),
web.get("/homeassistant/stats", api_hass.stats), web.get("/homeassistant/stats", api_hass.stats),
web.post("/homeassistant/options", api_hass.options), web.post("/homeassistant/options", api_hass.options),
web.post("/homeassistant/update", api_hass.update),
web.post("/homeassistant/restart", api_hass.restart), web.post("/homeassistant/restart", api_hass.restart),
web.post("/homeassistant/stop", api_hass.stop), web.post("/homeassistant/stop", api_hass.stop),
web.post("/homeassistant/start", api_hass.start), web.post("/homeassistant/start", api_hass.start),
web.post("/homeassistant/check", api_hass.check), web.post("/homeassistant/update", api_hass.update),
web.post("/homeassistant/rebuild", api_hass.rebuild), web.post("/homeassistant/rebuild", api_hass.rebuild),
web.post("/homeassistant/check", api_hass.check),
] ]
) )
@ -350,7 +355,12 @@ class RestAPI(CoreSysAttributes):
web.post("/core/api/{path:.+}", api_proxy.api), web.post("/core/api/{path:.+}", api_proxy.api),
web.get("/core/api/{path:.+}", api_proxy.api), web.get("/core/api/{path:.+}", api_proxy.api),
web.get("/core/api/", api_proxy.api), web.get("/core/api/", api_proxy.api),
# Remove with old Supervisor fallback ]
)
# Reroute from legacy
self.webapp.add_routes(
[
web.get("/homeassistant/api/websocket", api_proxy.websocket), web.get("/homeassistant/api/websocket", api_proxy.websocket),
web.get("/homeassistant/websocket", api_proxy.websocket), web.get("/homeassistant/websocket", api_proxy.websocket),
web.get("/homeassistant/api/stream", api_proxy.stream), web.get("/homeassistant/api/stream", api_proxy.stream),
@ -368,8 +378,10 @@ class RestAPI(CoreSysAttributes):
self.webapp.add_routes( self.webapp.add_routes(
[ [
web.get("/addons", api_addons.list), web.get("/addons", api_addons.list),
web.post("/addons/reload", api_addons.reload),
web.get("/addons/{addon}/info", api_addons.info), web.get("/addons/{addon}/info", api_addons.info),
web.get("/addons/{addon}/dark_icon", api_addons.dark_icon),
web.get("/addons/{addon}/logo", api_addons.logo),
web.get("/addons/{addon}/dark_logo", api_addons.dark_logo),
web.post("/addons/{addon}/uninstall", api_addons.uninstall), web.post("/addons/{addon}/uninstall", api_addons.uninstall),
web.post("/addons/{addon}/start", api_addons.start), web.post("/addons/{addon}/start", api_addons.start),
web.post("/addons/{addon}/stop", api_addons.stop), web.post("/addons/{addon}/stop", api_addons.stop),
@ -381,12 +393,6 @@ class RestAPI(CoreSysAttributes):
web.get("/addons/{addon}/options/config", api_addons.options_config), web.get("/addons/{addon}/options/config", api_addons.options_config),
web.post("/addons/{addon}/rebuild", api_addons.rebuild), web.post("/addons/{addon}/rebuild", api_addons.rebuild),
web.get("/addons/{addon}/logs", api_addons.logs), web.get("/addons/{addon}/logs", api_addons.logs),
web.get("/addons/{addon}/icon", api_addons.icon),
web.get("/addons/{addon}/dark_icon", api_addons.dark_icon),
web.get("/addons/{addon}/logo", api_addons.logo),
web.get("/addons/{addon}/dark_logo", api_addons.dark_logo),
web.get("/addons/{addon}/changelog", api_addons.changelog),
web.get("/addons/{addon}/documentation", api_addons.documentation),
web.post("/addons/{addon}/stdin", api_addons.stdin), web.post("/addons/{addon}/stdin", api_addons.stdin),
web.post("/addons/{addon}/security", api_addons.security), web.post("/addons/{addon}/security", api_addons.security),
web.get("/addons/{addon}/stats", api_addons.stats), web.get("/addons/{addon}/stats", api_addons.stats),
@ -414,21 +420,6 @@ class RestAPI(CoreSysAttributes):
self.webapp.add_routes( self.webapp.add_routes(
[ [
web.get("/snapshots", api_backups.list),
web.post("/snapshots/reload", api_backups.reload),
web.post("/snapshots/new/full", api_backups.backup_full),
web.post("/snapshots/new/partial", api_backups.backup_partial),
web.post("/snapshots/new/upload", api_backups.upload),
web.get("/snapshots/{slug}/info", api_backups.info),
web.delete("/snapshots/{slug}", api_backups.remove),
web.post("/snapshots/{slug}/restore/full", api_backups.restore_full),
web.post(
"/snapshots/{slug}/restore/partial",
api_backups.restore_partial,
),
web.get("/snapshots/{slug}/download", api_backups.download),
web.post("/snapshots/{slug}/remove", api_backups.remove),
# June 2021: /snapshots was renamed to /backups
web.get("/backups", api_backups.list), web.get("/backups", api_backups.list),
web.post("/backups/reload", api_backups.reload), web.post("/backups/reload", api_backups.reload),
web.post("/backups/new/full", api_backups.backup_full), web.post("/backups/new/full", api_backups.backup_full),
@ -523,6 +514,15 @@ class RestAPI(CoreSysAttributes):
web.get("/store/addons", api_store.addons_list), web.get("/store/addons", api_store.addons_list),
web.get("/store/addons/{addon}", api_store.addons_addon_info), web.get("/store/addons/{addon}", api_store.addons_addon_info),
web.get("/store/addons/{addon}/{version}", api_store.addons_addon_info), web.get("/store/addons/{addon}/{version}", api_store.addons_addon_info),
web.get("/store/addons/{addon}/icon", api_store.addons_addon_icon),
web.get("/store/addons/{addon}/logo", api_store.addons_addon_logo),
web.get(
"/store/addons/{addon}/changelog", api_store.addons_addon_changelog
),
web.get(
"/store/addons/{addon}/documentation",
api_store.addons_addon_documentation,
),
web.post( web.post(
"/store/addons/{addon}/install", api_store.addons_addon_install "/store/addons/{addon}/install", api_store.addons_addon_install
), ),
@ -551,8 +551,16 @@ class RestAPI(CoreSysAttributes):
# Reroute from legacy # Reroute from legacy
self.webapp.add_routes( self.webapp.add_routes(
[ [
web.post("/addons/reload", api_store.reload),
web.post("/addons/{addon}/install", api_store.addons_addon_install), web.post("/addons/{addon}/install", api_store.addons_addon_install),
web.post("/addons/{addon}/update", api_store.addons_addon_update), web.post("/addons/{addon}/update", api_store.addons_addon_update),
web.get("/addons/{addon}/icon", api_store.addons_addon_icon),
web.get("/addons/{addon}/logo", api_store.addons_addon_logo),
web.get("/addons/{addon}/changelog", api_store.addons_addon_changelog),
web.get(
"/addons/{addon}/documentation",
api_store.addons_addon_documentation,
),
] ]
) )

View File

@ -52,13 +52,11 @@ from ..const import (
ATTR_INGRESS_PANEL, ATTR_INGRESS_PANEL,
ATTR_INGRESS_PORT, ATTR_INGRESS_PORT,
ATTR_INGRESS_URL, ATTR_INGRESS_URL,
ATTR_INSTALLED,
ATTR_IP_ADDRESS, ATTR_IP_ADDRESS,
ATTR_KERNEL_MODULES, ATTR_KERNEL_MODULES,
ATTR_LOGO, ATTR_LOGO,
ATTR_LONG_DESCRIPTION, ATTR_LONG_DESCRIPTION,
ATTR_MACHINE, ATTR_MACHINE,
ATTR_MAINTAINER,
ATTR_MEMORY_LIMIT, ATTR_MEMORY_LIMIT,
ATTR_MEMORY_PERCENT, ATTR_MEMORY_PERCENT,
ATTR_MEMORY_USAGE, ATTR_MEMORY_USAGE,
@ -73,12 +71,10 @@ from ..const import (
ATTR_PROTECTED, ATTR_PROTECTED,
ATTR_PWNED, ATTR_PWNED,
ATTR_RATING, ATTR_RATING,
ATTR_REPOSITORIES,
ATTR_REPOSITORY, ATTR_REPOSITORY,
ATTR_SCHEMA, ATTR_SCHEMA,
ATTR_SERVICES, ATTR_SERVICES,
ATTR_SLUG, ATTR_SLUG,
ATTR_SOURCE,
ATTR_STAGE, ATTR_STAGE,
ATTR_STARTUP, ATTR_STARTUP,
ATTR_STATE, ATTR_STATE,
@ -95,18 +91,14 @@ from ..const import (
ATTR_VIDEO, ATTR_VIDEO,
ATTR_WATCHDOG, ATTR_WATCHDOG,
ATTR_WEBUI, ATTR_WEBUI,
CONTENT_TYPE_BINARY,
CONTENT_TYPE_PNG,
CONTENT_TYPE_TEXT,
REQUEST_FROM, REQUEST_FROM,
AddonBoot, AddonBoot,
AddonState,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..docker.stats import DockerStats from ..docker.stats import DockerStats
from ..exceptions import APIError, APIForbidden, PwnedError, PwnedSecret from ..exceptions import APIError, APIForbidden, PwnedError, PwnedSecret
from ..validate import docker_ports from ..validate import docker_ports
from .const import ATTR_SIGNED from .const import ATTR_SIGNED, CONTENT_TYPE_BINARY, CONTENT_TYPE_PNG
from .utils import api_process, api_process_raw, api_validate, json_loads from .utils import api_process, api_process_raw, api_validate, json_loads
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@ -133,7 +125,7 @@ SCHEMA_SECURITY = vol.Schema({vol.Optional(ATTR_PROTECTED): vol.Boolean()})
class APIAddons(CoreSysAttributes): class APIAddons(CoreSysAttributes):
"""Handle RESTful API for add-on functions.""" """Handle RESTful API for add-on functions."""
def _extract_addon(self, request: web.Request) -> AnyAddon: def _extract_addon(self, request: web.Request) -> Addon:
"""Return addon, throw an exception it it doesn't exist.""" """Return addon, throw an exception it it doesn't exist."""
addon_slug: str = request.match_info.get("addon") addon_slug: str = request.match_info.get("addon")
@ -147,13 +139,9 @@ class APIAddons(CoreSysAttributes):
addon = self.sys_addons.get(addon_slug) addon = self.sys_addons.get(addon_slug)
if not addon: if not addon:
raise APIError(f"Addon {addon_slug} does not exist") raise APIError(f"Addon {addon_slug} does not exist")
return addon
def _extract_addon_installed(self, request: web.Request) -> Addon:
addon = self._extract_addon(request)
if not isinstance(addon, Addon) or not addon.is_installed: if not isinstance(addon, Addon) or not addon.is_installed:
raise APIError("Addon is not installed") raise APIError("Addon is not installed")
return addon return addon
@api_process @api_process
@ -166,35 +154,23 @@ class APIAddons(CoreSysAttributes):
ATTR_DESCRIPTON: addon.description, ATTR_DESCRIPTON: addon.description,
ATTR_ADVANCED: addon.advanced, ATTR_ADVANCED: addon.advanced,
ATTR_STAGE: addon.stage, ATTR_STAGE: addon.stage,
ATTR_VERSION: addon.version if addon.is_installed else None, ATTR_VERSION: addon.version,
ATTR_VERSION_LATEST: addon.latest_version, ATTR_VERSION_LATEST: addon.latest_version,
ATTR_UPDATE_AVAILABLE: addon.need_update ATTR_UPDATE_AVAILABLE: addon.need_update,
if addon.is_installed
else False,
ATTR_INSTALLED: addon.is_installed,
ATTR_AVAILABLE: addon.available, ATTR_AVAILABLE: addon.available,
ATTR_DETACHED: addon.is_detached, ATTR_DETACHED: addon.is_detached,
ATTR_HOMEASSISTANT: addon.homeassistant_version, ATTR_HOMEASSISTANT: addon.homeassistant_version,
ATTR_STATE: addon.state,
ATTR_REPOSITORY: addon.repository, ATTR_REPOSITORY: addon.repository,
ATTR_BUILD: addon.need_build, ATTR_BUILD: addon.need_build,
ATTR_URL: addon.url, ATTR_URL: addon.url,
ATTR_ICON: addon.with_icon, ATTR_ICON: addon.with_icon,
ATTR_LOGO: addon.with_logo, ATTR_LOGO: addon.with_logo,
} }
for addon in self.sys_addons.all for addon in self.sys_addons.installed
] ]
data_repositories = [ return {ATTR_ADDONS: data_addons}
{
ATTR_SLUG: repository.slug,
ATTR_NAME: repository.name,
ATTR_SOURCE: repository.source,
ATTR_URL: repository.url,
ATTR_MAINTAINER: repository.maintainer,
}
for repository in self.sys_store.all
]
return {ATTR_ADDONS: data_addons, ATTR_REPOSITORIES: data_repositories}
@api_process @api_process
async def reload(self, request: web.Request) -> None: async def reload(self, request: web.Request) -> None:
@ -215,11 +191,8 @@ class APIAddons(CoreSysAttributes):
ATTR_LONG_DESCRIPTION: addon.long_description, ATTR_LONG_DESCRIPTION: addon.long_description,
ATTR_ADVANCED: addon.advanced, ATTR_ADVANCED: addon.advanced,
ATTR_STAGE: addon.stage, ATTR_STAGE: addon.stage,
ATTR_AUTO_UPDATE: None,
ATTR_REPOSITORY: addon.repository, ATTR_REPOSITORY: addon.repository,
ATTR_VERSION: None,
ATTR_VERSION_LATEST: addon.latest_version, ATTR_VERSION_LATEST: addon.latest_version,
ATTR_UPDATE_AVAILABLE: False,
ATTR_PROTECTED: addon.protected, ATTR_PROTECTED: addon.protected,
ATTR_RATING: rating_security(addon), ATTR_RATING: rating_security(addon),
ATTR_BOOT: addon.boot, ATTR_BOOT: addon.boot,
@ -229,7 +202,6 @@ class APIAddons(CoreSysAttributes):
ATTR_MACHINE: addon.supported_machine, ATTR_MACHINE: addon.supported_machine,
ATTR_HOMEASSISTANT: addon.homeassistant_version, ATTR_HOMEASSISTANT: addon.homeassistant_version,
ATTR_URL: addon.url, ATTR_URL: addon.url,
ATTR_STATE: AddonState.UNKNOWN,
ATTR_DETACHED: addon.is_detached, ATTR_DETACHED: addon.is_detached,
ATTR_AVAILABLE: addon.available, ATTR_AVAILABLE: addon.available,
ATTR_BUILD: addon.need_build, ATTR_BUILD: addon.need_build,
@ -242,13 +214,11 @@ class APIAddons(CoreSysAttributes):
ATTR_PRIVILEGED: addon.privileged, ATTR_PRIVILEGED: addon.privileged,
ATTR_FULL_ACCESS: addon.with_full_access, ATTR_FULL_ACCESS: addon.with_full_access,
ATTR_APPARMOR: addon.apparmor, ATTR_APPARMOR: addon.apparmor,
ATTR_DEVICES: addon.static_devices,
ATTR_ICON: addon.with_icon, ATTR_ICON: addon.with_icon,
ATTR_LOGO: addon.with_logo, ATTR_LOGO: addon.with_logo,
ATTR_CHANGELOG: addon.with_changelog, ATTR_CHANGELOG: addon.with_changelog,
ATTR_DOCUMENTATION: addon.with_documentation, ATTR_DOCUMENTATION: addon.with_documentation,
ATTR_STDIN: addon.with_stdin, ATTR_STDIN: addon.with_stdin,
ATTR_WEBUI: None,
ATTR_HASSIO_API: addon.access_hassio_api, ATTR_HASSIO_API: addon.access_hassio_api,
ATTR_HASSIO_ROLE: addon.hassio_role, ATTR_HASSIO_ROLE: addon.hassio_role,
ATTR_AUTH_API: addon.access_auth_api, ATTR_AUTH_API: addon.access_auth_api,
@ -262,49 +232,35 @@ class APIAddons(CoreSysAttributes):
ATTR_DOCKER_API: addon.access_docker_api, ATTR_DOCKER_API: addon.access_docker_api,
ATTR_VIDEO: addon.with_video, ATTR_VIDEO: addon.with_video,
ATTR_AUDIO: addon.with_audio, ATTR_AUDIO: addon.with_audio,
ATTR_AUDIO_INPUT: None,
ATTR_AUDIO_OUTPUT: None,
ATTR_STARTUP: addon.startup, ATTR_STARTUP: addon.startup,
ATTR_SERVICES: _pretty_services(addon), ATTR_SERVICES: _pretty_services(addon),
ATTR_DISCOVERY: addon.discovery, ATTR_DISCOVERY: addon.discovery,
ATTR_IP_ADDRESS: None,
ATTR_TRANSLATIONS: addon.translations, ATTR_TRANSLATIONS: addon.translations,
ATTR_INGRESS: addon.with_ingress, ATTR_INGRESS: addon.with_ingress,
ATTR_SIGNED: addon.signed, ATTR_SIGNED: addon.signed,
ATTR_INGRESS_ENTRY: None, ATTR_STATE: addon.state,
ATTR_INGRESS_URL: None, ATTR_WEBUI: addon.webui,
ATTR_INGRESS_PORT: None, ATTR_INGRESS_ENTRY: addon.ingress_entry,
ATTR_INGRESS_PANEL: None, ATTR_INGRESS_URL: addon.ingress_url,
ATTR_WATCHDOG: None, ATTR_INGRESS_PORT: addon.ingress_port,
ATTR_INGRESS_PANEL: addon.ingress_panel,
ATTR_AUDIO_INPUT: addon.audio_input,
ATTR_AUDIO_OUTPUT: addon.audio_output,
ATTR_AUTO_UPDATE: addon.auto_update,
ATTR_IP_ADDRESS: str(addon.ip_address),
ATTR_VERSION: addon.version,
ATTR_UPDATE_AVAILABLE: addon.need_update,
ATTR_WATCHDOG: addon.watchdog,
ATTR_DEVICES: addon.static_devices
+ [device.path for device in addon.devices],
} }
if isinstance(addon, Addon) and addon.is_installed:
data.update(
{
ATTR_STATE: addon.state,
ATTR_WEBUI: addon.webui,
ATTR_INGRESS_ENTRY: addon.ingress_entry,
ATTR_INGRESS_URL: addon.ingress_url,
ATTR_INGRESS_PORT: addon.ingress_port,
ATTR_INGRESS_PANEL: addon.ingress_panel,
ATTR_AUDIO_INPUT: addon.audio_input,
ATTR_AUDIO_OUTPUT: addon.audio_output,
ATTR_AUTO_UPDATE: addon.auto_update,
ATTR_IP_ADDRESS: str(addon.ip_address),
ATTR_VERSION: addon.version,
ATTR_UPDATE_AVAILABLE: addon.need_update,
ATTR_WATCHDOG: addon.watchdog,
ATTR_DEVICES: addon.static_devices
+ [device.path for device in addon.devices],
}
)
return data return data
@api_process @api_process
async def options(self, request: web.Request) -> None: async def options(self, request: web.Request) -> None:
"""Store user options for add-on.""" """Store user options for add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
# Update secrets for validation # Update secrets for validation
await self.sys_homeassistant.secrets.reload() await self.sys_homeassistant.secrets.reload()
@ -339,7 +295,7 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def options_validate(self, request: web.Request) -> None: async def options_validate(self, request: web.Request) -> None:
"""Validate user options for add-on.""" """Validate user options for add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
data = {ATTR_MESSAGE: "", ATTR_VALID: True, ATTR_PWNED: False} data = {ATTR_MESSAGE: "", ATTR_VALID: True, ATTR_PWNED: False}
options = await request.json(loads=json_loads) or addon.options options = await request.json(loads=json_loads) or addon.options
@ -381,7 +337,7 @@ class APIAddons(CoreSysAttributes):
slug: str = request.match_info.get("addon") slug: str = request.match_info.get("addon")
if slug != "self": if slug != "self":
raise APIForbidden("This can be only read by the Add-on itself!") raise APIForbidden("This can be only read by the Add-on itself!")
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
# Lookup/reload secrets # Lookup/reload secrets
await self.sys_homeassistant.secrets.reload() await self.sys_homeassistant.secrets.reload()
@ -393,7 +349,7 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def security(self, request: web.Request) -> None: async def security(self, request: web.Request) -> None:
"""Store security options for add-on.""" """Store security options for add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
body: dict[str, Any] = await api_validate(SCHEMA_SECURITY, request) body: dict[str, Any] = await api_validate(SCHEMA_SECURITY, request)
if ATTR_PROTECTED in body: if ATTR_PROTECTED in body:
@ -405,7 +361,7 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
async def stats(self, request: web.Request) -> dict[str, Any]: async def stats(self, request: web.Request) -> dict[str, Any]:
"""Return resource information.""" """Return resource information."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
stats: DockerStats = await addon.stats() stats: DockerStats = await addon.stats()
@ -423,37 +379,37 @@ class APIAddons(CoreSysAttributes):
@api_process @api_process
def uninstall(self, request: web.Request) -> Awaitable[None]: def uninstall(self, request: web.Request) -> Awaitable[None]:
"""Uninstall add-on.""" """Uninstall add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
return asyncio.shield(addon.uninstall()) return asyncio.shield(addon.uninstall())
@api_process @api_process
def start(self, request: web.Request) -> Awaitable[None]: def start(self, request: web.Request) -> Awaitable[None]:
"""Start add-on.""" """Start add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
return asyncio.shield(addon.start()) return asyncio.shield(addon.start())
@api_process @api_process
def stop(self, request: web.Request) -> Awaitable[None]: def stop(self, request: web.Request) -> Awaitable[None]:
"""Stop add-on.""" """Stop add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
return asyncio.shield(addon.stop()) return asyncio.shield(addon.stop())
@api_process @api_process
def restart(self, request: web.Request) -> Awaitable[None]: def restart(self, request: web.Request) -> Awaitable[None]:
"""Restart add-on.""" """Restart add-on."""
addon: Addon = self._extract_addon_installed(request) addon: Addon = self._extract_addon(request)
return asyncio.shield(addon.restart()) return asyncio.shield(addon.restart())
@api_process @api_process
def rebuild(self, request: web.Request) -> Awaitable[None]: def rebuild(self, request: web.Request) -> Awaitable[None]:
"""Rebuild local build add-on.""" """Rebuild local build add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
return asyncio.shield(addon.rebuild()) return asyncio.shield(addon.rebuild())
@api_process_raw(CONTENT_TYPE_BINARY) @api_process_raw(CONTENT_TYPE_BINARY)
def logs(self, request: web.Request) -> Awaitable[bytes]: def logs(self, request: web.Request) -> Awaitable[bytes]:
"""Return logs from add-on.""" """Return logs from add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
return addon.logs() return addon.logs()
@api_process_raw(CONTENT_TYPE_PNG) @api_process_raw(CONTENT_TYPE_PNG)
@ -504,30 +460,10 @@ class APIAddons(CoreSysAttributes):
with addon.path_logo.open("rb") as png: with addon.path_logo.open("rb") as png:
return png.read() return png.read()
@api_process_raw(CONTENT_TYPE_TEXT)
async def changelog(self, request: web.Request) -> str:
"""Return changelog from add-on."""
addon = self._extract_addon(request)
if not addon.with_changelog:
raise APIError(f"No changelog found for add-on {addon.slug}!")
with addon.path_changelog.open("r") as changelog:
return changelog.read()
@api_process_raw(CONTENT_TYPE_TEXT)
async def documentation(self, request: web.Request) -> str:
"""Return documentation from add-on."""
addon = self._extract_addon(request)
if not addon.with_documentation:
raise APIError(f"No documentation found for add-on {addon.slug}!")
with addon.path_documentation.open("r") as documentation:
return documentation.read()
@api_process @api_process
async def stdin(self, request: web.Request) -> None: async def stdin(self, request: web.Request) -> None:
"""Write to stdin of add-on.""" """Write to stdin of add-on."""
addon = self._extract_addon_installed(request) addon = self._extract_addon(request)
if not addon.with_stdin: if not addon.with_stdin:
raise APIError(f"STDIN not supported the {addon.slug} add-on") raise APIError(f"STDIN not supported the {addon.slug} add-on")
@ -535,6 +471,6 @@ class APIAddons(CoreSysAttributes):
await asyncio.shield(addon.write_stdin(data)) await asyncio.shield(addon.write_stdin(data))
def _pretty_services(addon: AnyAddon) -> list[str]: def _pretty_services(addon: Addon) -> list[str]:
"""Return a simplified services role list.""" """Return a simplified services role list."""
return [f"{name}:{access}" for name, access in addon.services_role.items()] return [f"{name}:{access}" for name, access in addon.services_role.items()]

View File

@ -29,12 +29,12 @@ from ..const import (
ATTR_VERSION, ATTR_VERSION,
ATTR_VERSION_LATEST, ATTR_VERSION_LATEST,
ATTR_VOLUME, ATTR_VOLUME,
CONTENT_TYPE_BINARY,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIError from ..exceptions import APIError
from ..host.sound import StreamType from ..host.sound import StreamType
from ..validate import version_tag from ..validate import version_tag
from .const import CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -8,15 +8,10 @@ from aiohttp.web_exceptions import HTTPUnauthorized
import voluptuous as vol import voluptuous as vol
from ..addons.addon import Addon from ..addons.addon import Addon
from ..const import ( from ..const import ATTR_PASSWORD, ATTR_USERNAME, REQUEST_FROM
ATTR_PASSWORD,
ATTR_USERNAME,
CONTENT_TYPE_JSON,
CONTENT_TYPE_URL,
REQUEST_FROM,
)
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIForbidden from ..exceptions import APIForbidden
from .const import CONTENT_TYPE_JSON, CONTENT_TYPE_URL
from .utils import api_process, api_validate from .utils import api_process, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -26,17 +26,18 @@ from ..const import (
ATTR_SLUG, ATTR_SLUG,
ATTR_TYPE, ATTR_TYPE,
ATTR_VERSION, ATTR_VERSION,
CONTENT_TYPE_TAR,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIError from ..exceptions import APIError
from .const import CONTENT_TYPE_TAR
from .utils import api_process, api_validate from .utils import api_process, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
RE_SLUGIFY_NAME = re.compile(r"[^A-Za-z0-9]+") RE_SLUGIFY_NAME = re.compile(r"[^A-Za-z0-9]+")
# Backwards compatible / Remove 2022.08 # Backwards compatible
# Remove: 2022.08
_ALL_FOLDERS = ALL_FOLDERS + [FOLDER_HOMEASSISTANT] _ALL_FOLDERS = ALL_FOLDERS + [FOLDER_HOMEASSISTANT]
# pylint: disable=no-value-for-parameter # pylint: disable=no-value-for-parameter

View File

@ -1,5 +1,17 @@
"""Const for API.""" """Const for API."""
CONTENT_TYPE_BINARY = "application/octet-stream"
CONTENT_TYPE_JSON = "application/json"
CONTENT_TYPE_PNG = "image/png"
CONTENT_TYPE_TAR = "application/tar"
CONTENT_TYPE_TEXT = "text/plain"
CONTENT_TYPE_URL = "application/x-www-form-urlencoded"
COOKIE_INGRESS = "ingress_session"
HEADER_TOKEN_OLD = "X-Hassio-Key"
HEADER_TOKEN = "X-Supervisor-Token"
ATTR_APPARMOR_VERSION = "apparmor_version" ATTR_APPARMOR_VERSION = "apparmor_version"
ATTR_AGENT_VERSION = "agent_version" ATTR_AGENT_VERSION = "agent_version"
ATTR_AVAILABLE_UPDATES = "available_updates" ATTR_AVAILABLE_UPDATES = "available_updates"
@ -19,4 +31,9 @@ ATTR_SIGNED = "signed"
ATTR_STARTUP_TIME = "startup_time" ATTR_STARTUP_TIME = "startup_time"
ATTR_UPDATE_TYPE = "update_type" ATTR_UPDATE_TYPE = "update_type"
ATTR_USE_NTP = "use_ntp" ATTR_USE_NTP = "use_ntp"
ATTR_USE_RTC = "use_rtc" ATTR_BY_ID = "by_id"
ATTR_SUBSYSTEM = "subsystem"
ATTR_SYSFS = "sysfs"
ATTR_DEV_PATH = "dev_path"
ATTR_ATTRIBUTES = "attributes"
ATTR_CHILDREN = "children"

View File

@ -21,12 +21,11 @@ from ..const import (
ATTR_UPDATE_AVAILABLE, ATTR_UPDATE_AVAILABLE,
ATTR_VERSION, ATTR_VERSION,
ATTR_VERSION_LATEST, ATTR_VERSION_LATEST,
CONTENT_TYPE_BINARY,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIError from ..exceptions import APIError
from ..validate import dns_server_list, version_tag from ..validate import dns_server_list, version_tag
from .const import ATTR_FALLBACK, ATTR_LLMNR, ATTR_MDNS from .const import ATTR_FALLBACK, ATTR_LLMNR, ATTR_MDNS, CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -6,14 +6,15 @@ from aiohttp import web
from ..const import ATTR_AUDIO, ATTR_DEVICES, ATTR_INPUT, ATTR_NAME, ATTR_OUTPUT from ..const import ATTR_AUDIO, ATTR_DEVICES, ATTR_INPUT, ATTR_NAME, ATTR_OUTPUT
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..hardware.const import ( from ..hardware.data import Device
from .const import (
ATTR_ATTRIBUTES, ATTR_ATTRIBUTES,
ATTR_BY_ID, ATTR_BY_ID,
ATTR_CHILDREN,
ATTR_DEV_PATH, ATTR_DEV_PATH,
ATTR_SUBSYSTEM, ATTR_SUBSYSTEM,
ATTR_SYSFS, ATTR_SYSFS,
) )
from ..hardware.data import Device
from .utils import api_process from .utils import api_process
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@ -28,6 +29,7 @@ def device_struct(device: Device) -> dict[str, Any]:
ATTR_SUBSYSTEM: device.subsystem, ATTR_SUBSYSTEM: device.subsystem,
ATTR_BY_ID: device.by_id, ATTR_BY_ID: device.by_id,
ATTR_ATTRIBUTES: device.attributes, ATTR_ATTRIBUTES: device.attributes,
ATTR_CHILDREN: device.children,
} }

View File

@ -29,13 +29,12 @@ from ..const import (
ATTR_UPDATE_AVAILABLE, ATTR_UPDATE_AVAILABLE,
ATTR_VERSION, ATTR_VERSION,
ATTR_VERSION_LATEST, ATTR_VERSION_LATEST,
ATTR_WAIT_BOOT,
ATTR_WATCHDOG, ATTR_WATCHDOG,
CONTENT_TYPE_BINARY,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIError from ..exceptions import APIError
from ..validate import docker_image, network_port, version_tag from ..validate import docker_image, network_port, version_tag
from .const import CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@ -48,7 +47,6 @@ SCHEMA_OPTIONS = vol.Schema(
vol.Optional(ATTR_PORT): network_port, vol.Optional(ATTR_PORT): network_port,
vol.Optional(ATTR_SSL): vol.Boolean(), vol.Optional(ATTR_SSL): vol.Boolean(),
vol.Optional(ATTR_WATCHDOG): vol.Boolean(), vol.Optional(ATTR_WATCHDOG): vol.Boolean(),
vol.Optional(ATTR_WAIT_BOOT): vol.All(vol.Coerce(int), vol.Range(min=60)),
vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(str), vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(str),
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(str), vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(str),
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(str), vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(str),
@ -81,11 +79,8 @@ class APIHomeAssistant(CoreSysAttributes):
ATTR_PORT: self.sys_homeassistant.api_port, ATTR_PORT: self.sys_homeassistant.api_port,
ATTR_SSL: self.sys_homeassistant.api_ssl, ATTR_SSL: self.sys_homeassistant.api_ssl,
ATTR_WATCHDOG: self.sys_homeassistant.watchdog, ATTR_WATCHDOG: self.sys_homeassistant.watchdog,
ATTR_WAIT_BOOT: self.sys_homeassistant.wait_boot,
ATTR_AUDIO_INPUT: self.sys_homeassistant.audio_input, ATTR_AUDIO_INPUT: self.sys_homeassistant.audio_input,
ATTR_AUDIO_OUTPUT: self.sys_homeassistant.audio_output, ATTR_AUDIO_OUTPUT: self.sys_homeassistant.audio_output,
# Remove end of Q3 2020
"last_version": self.sys_homeassistant.latest_version,
} }
@api_process @api_process
@ -108,9 +103,6 @@ class APIHomeAssistant(CoreSysAttributes):
if ATTR_WATCHDOG in body: if ATTR_WATCHDOG in body:
self.sys_homeassistant.watchdog = body[ATTR_WATCHDOG] self.sys_homeassistant.watchdog = body[ATTR_WATCHDOG]
if ATTR_WAIT_BOOT in body:
self.sys_homeassistant.wait_boot = body[ATTR_WAIT_BOOT]
if ATTR_REFRESH_TOKEN in body: if ATTR_REFRESH_TOKEN in body:
self.sys_homeassistant.refresh_token = body[ATTR_REFRESH_TOKEN] self.sys_homeassistant.refresh_token = body[ATTR_REFRESH_TOKEN]

View File

@ -22,7 +22,6 @@ from ..const import (
ATTR_SERVICES, ATTR_SERVICES,
ATTR_STATE, ATTR_STATE,
ATTR_TIMEZONE, ATTR_TIMEZONE,
CONTENT_TYPE_BINARY,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from .const import ( from .const import (
@ -36,7 +35,7 @@ from .const import (
ATTR_LLMNR_HOSTNAME, ATTR_LLMNR_HOSTNAME,
ATTR_STARTUP_TIME, ATTR_STARTUP_TIME,
ATTR_USE_NTP, ATTR_USE_NTP,
ATTR_USE_RTC, CONTENT_TYPE_BINARY,
) )
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
@ -70,7 +69,6 @@ class APIHost(CoreSysAttributes):
ATTR_DT_UTC: self.sys_host.info.dt_utc, ATTR_DT_UTC: self.sys_host.info.dt_utc,
ATTR_DT_SYNCHRONIZED: self.sys_host.info.dt_synchronized, ATTR_DT_SYNCHRONIZED: self.sys_host.info.dt_synchronized,
ATTR_USE_NTP: self.sys_host.info.use_ntp, ATTR_USE_NTP: self.sys_host.info.use_ntp,
ATTR_USE_RTC: self.sys_host.info.use_rtc,
ATTR_STARTUP_TIME: self.sys_host.info.startup_time, ATTR_STARTUP_TIME: self.sys_host.info.startup_time,
ATTR_BOOT_TIMESTAMP: self.sys_host.info.boot_timestamp, ATTR_BOOT_TIMESTAMP: self.sys_host.info.boot_timestamp,
ATTR_BROADCAST_LLMNR: self.sys_host.info.broadcast_llmnr, ATTR_BROADCAST_LLMNR: self.sys_host.info.broadcast_llmnr,
@ -101,11 +99,7 @@ class APIHost(CoreSysAttributes):
@api_process @api_process
def reload(self, request): def reload(self, request):
"""Reload host data.""" """Reload host data."""
return asyncio.shield( return asyncio.shield(self.sys_host.reload())
asyncio.wait(
[self.sys_host.reload(), self.sys_resolution.evaluate.evaluate_system()]
)
)
@api_process @api_process
async def services(self, request): async def services(self, request):

View File

@ -22,11 +22,9 @@ from ..const import (
ATTR_PANELS, ATTR_PANELS,
ATTR_SESSION, ATTR_SESSION,
ATTR_TITLE, ATTR_TITLE,
COOKIE_INGRESS,
HEADER_TOKEN,
HEADER_TOKEN_OLD,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from .const import COOKIE_INGRESS, HEADER_TOKEN, HEADER_TOKEN_OLD
from .utils import api_process, api_validate, require_home_assistant from .utils import api_process, api_validate, require_home_assistant
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -77,7 +77,6 @@ ADDONS_ROLE_ACCESS = {
r"^(?:" r"^(?:"
r"|/.+/info" r"|/.+/info"
r"|/backups.*" r"|/backups.*"
r"|/snapshots.*"
r")$" r")$"
), ),
ROLE_MANAGER: re.compile( ROLE_MANAGER: re.compile(

View File

@ -18,11 +18,11 @@ from ..const import (
ATTR_UPDATE_AVAILABLE, ATTR_UPDATE_AVAILABLE,
ATTR_VERSION, ATTR_VERSION,
ATTR_VERSION_LATEST, ATTR_VERSION_LATEST,
CONTENT_TYPE_BINARY,
) )
from ..coresys import CoreSysAttributes from ..coresys import CoreSysAttributes
from ..exceptions import APIError from ..exceptions import APIError
from ..validate import version_tag from ..validate import version_tag
from .const import CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -1,14 +1,14 @@
function loadES5() { function loadES5() {
var el = document.createElement('script'); var el = document.createElement('script');
el.src = '/api/hassio/app/frontend_es5/entrypoint.073e9cdc.js'; el.src = '/api/hassio/app/frontend_es5/entrypoint.f8f83860.js';
document.body.appendChild(el); document.body.appendChild(el);
} }
if (/.*Version\/(?:11|12)(?:\.\d+)*.*Safari\//.test(navigator.userAgent)) { if (/.*Version\/(?:11|12)(?:\.\d+)*.*Safari\//.test(navigator.userAgent)) {
loadES5(); loadES5();
} else { } else {
try { try {
new Function("import('/api/hassio/app/frontend_latest/entrypoint.e7839dce.js')")(); new Function("import('/api/hassio/app/frontend_latest/entrypoint.b6cf778b.js')")();
} catch (err) { } catch (err) {
loadES5(); loadES5();
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
{ {
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.073e9cdc.js" "entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.f8f83860.js"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -55,4 +55,4 @@
--app-header-border-bottom: 1px solid var(--divider-color); --app-header-border-bottom: 1px solid var(--divider-color);
} }
`}}]}}),i.oi)}}]); `}}]}}),i.oi)}}]);
//# sourceMappingURL=99edc920.js.map //# sourceMappingURL=5d68c82a.js.map

Binary file not shown.

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

File diff suppressed because one or more lines are too long

View File

@ -108,4 +108,4 @@
padding-bottom: 8px; padding-bottom: 8px;
} }
`}}]}}),i.oi)},4450:(e,t,r)=>{r.d(t,{I:()=>i,H:()=>n});const i=(0,r(4516).Z)(((e,t)=>t.some((t=>e.includes(t))))),n=(e,t)=>{if(t.startsWith("# Changelog")&&(t=t.substr(12,t.length)),t.includes(`# ${e.version}`)&&t.includes(`# ${e.version_latest}`)){const r=t.split(`# ${e.version}`)[0];r.includes(`# ${e.version_latest}`)&&(t=r)}return t}}}]); `}}]}}),i.oi)},4450:(e,t,r)=>{r.d(t,{I:()=>i,H:()=>n});const i=(0,r(4516).Z)(((e,t)=>t.some((t=>e.includes(t))))),n=(e,t)=>{if(t.startsWith("# Changelog")&&(t=t.substr(12,t.length)),t.includes(`# ${e.version}`)&&t.includes(`# ${e.version_latest}`)){const r=t.split(`# ${e.version}`)[0];r.includes(`# ${e.version_latest}`)&&(t=r)}return t}}}]);
//# sourceMappingURL=fe3336e8.js.map //# sourceMappingURL=bf6a06c2.js.map

Binary file not shown.

File diff suppressed because one or more lines are too long

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

View File

@ -1,3 +1,3 @@
{ {
"entrypoint.js": "/api/hassio/app/frontend_latest/entrypoint.e7839dce.js" "entrypoint.js": "/api/hassio/app/frontend_latest/entrypoint.b6cf778b.js"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@ import voluptuous as vol
from ..addons import AnyAddon from ..addons import AnyAddon
from ..addons.utils import rating_security from ..addons.utils import rating_security
from ..api.const import ATTR_SIGNED from ..api.const import ATTR_SIGNED
from ..api.utils import api_process, api_validate from ..api.utils import api_process, api_process_raw, api_validate
from ..const import ( from ..const import (
ATTR_ADDONS, ATTR_ADDONS,
ATTR_ADVANCED, ATTR_ADVANCED,
@ -53,6 +53,7 @@ from ..exceptions import APIError, APIForbidden
from ..store.addon import AddonStore from ..store.addon import AddonStore
from ..store.repository import Repository from ..store.repository import Repository
from ..store.validate import validate_repository from ..store.validate import validate_repository
from .const import CONTENT_TYPE_PNG, CONTENT_TYPE_TEXT
SCHEMA_UPDATE = vol.Schema( SCHEMA_UPDATE = vol.Schema(
{ {
@ -206,6 +207,46 @@ class APIStore(CoreSysAttributes):
addon: AddonStore = self._extract_addon(request) addon: AddonStore = self._extract_addon(request)
return self._generate_addon_information(addon, True) return self._generate_addon_information(addon, True)
@api_process_raw(CONTENT_TYPE_PNG)
async def addons_addon_icon(self, request: web.Request) -> bytes:
"""Return icon from add-on."""
addon = self._extract_addon(request)
if not addon.with_icon:
raise APIError(f"No icon found for add-on {addon.slug}!")
with addon.path_icon.open("rb") as png:
return png.read()
@api_process_raw(CONTENT_TYPE_PNG)
async def addons_addon_logo(self, request: web.Request) -> bytes:
"""Return logo from add-on."""
addon = self._extract_addon(request)
if not addon.with_logo:
raise APIError(f"No logo found for add-on {addon.slug}!")
with addon.path_logo.open("rb") as png:
return png.read()
@api_process_raw(CONTENT_TYPE_TEXT)
async def addons_addon_changelog(self, request: web.Request) -> str:
"""Return changelog from add-on."""
addon = self._extract_addon(request)
if not addon.with_changelog:
raise APIError(f"No changelog found for add-on {addon.slug}!")
with addon.path_changelog.open("r") as changelog:
return changelog.read()
@api_process_raw(CONTENT_TYPE_TEXT)
async def addons_addon_documentation(self, request: web.Request) -> str:
"""Return documentation from add-on."""
addon = self._extract_addon(request)
if not addon.with_documentation:
raise APIError(f"No documentation found for add-on {addon.slug}!")
with addon.path_documentation.open("r") as documentation:
return documentation.read()
@api_process @api_process
async def repositories_list(self, request: web.Request) -> list[dict[str, Any]]: async def repositories_list(self, request: web.Request) -> list[dict[str, Any]]:
"""Return all repositories.""" """Return all repositories."""

View File

@ -17,14 +17,12 @@ from ..const import (
ATTR_CPU_PERCENT, ATTR_CPU_PERCENT,
ATTR_DEBUG, ATTR_DEBUG,
ATTR_DEBUG_BLOCK, ATTR_DEBUG_BLOCK,
ATTR_DESCRIPTON,
ATTR_DIAGNOSTICS, ATTR_DIAGNOSTICS,
ATTR_FORCE_SECURITY, ATTR_FORCE_SECURITY,
ATTR_HEALTHY, ATTR_HEALTHY,
ATTR_ICON, ATTR_ICON,
ATTR_IP_ADDRESS, ATTR_IP_ADDRESS,
ATTR_LOGGING, ATTR_LOGGING,
ATTR_LOGO,
ATTR_MEMORY_LIMIT, ATTR_MEMORY_LIMIT,
ATTR_MEMORY_PERCENT, ATTR_MEMORY_PERCENT,
ATTR_MEMORY_USAGE, ATTR_MEMORY_USAGE,
@ -40,7 +38,6 @@ from ..const import (
ATTR_VERSION, ATTR_VERSION,
ATTR_VERSION_LATEST, ATTR_VERSION_LATEST,
ATTR_WAIT_BOOT, ATTR_WAIT_BOOT,
CONTENT_TYPE_BINARY,
LogLevel, LogLevel,
UpdateChannel, UpdateChannel,
) )
@ -49,6 +46,7 @@ from ..exceptions import APIError
from ..store.validate import repositories from ..store.validate import repositories
from ..utils.validate import validate_timezone from ..utils.validate import validate_timezone
from ..validate import version_tag, wait_boot from ..validate import version_tag, wait_boot
from .const import CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate from .utils import api_process, api_process_raw, api_validate
_LOGGER: logging.Logger = logging.getLogger(__name__) _LOGGER: logging.Logger = logging.getLogger(__name__)
@ -83,23 +81,6 @@ class APISupervisor(CoreSysAttributes):
@api_process @api_process
async def info(self, request: web.Request) -> dict[str, Any]: async def info(self, request: web.Request) -> dict[str, Any]:
"""Return host information.""" """Return host information."""
list_addons = []
for addon in self.sys_addons.installed:
list_addons.append(
{
ATTR_NAME: addon.name,
ATTR_SLUG: addon.slug,
ATTR_DESCRIPTON: addon.description,
ATTR_STATE: addon.state,
ATTR_VERSION: addon.version,
ATTR_VERSION_LATEST: addon.latest_version,
ATTR_UPDATE_AVAILABLE: addon.need_update,
ATTR_REPOSITORY: addon.repository,
ATTR_ICON: addon.with_icon,
ATTR_LOGO: addon.with_logo,
}
)
return { return {
ATTR_VERSION: self.sys_supervisor.version, ATTR_VERSION: self.sys_supervisor.version,
ATTR_VERSION_LATEST: self.sys_supervisor.latest_version, ATTR_VERSION_LATEST: self.sys_supervisor.latest_version,
@ -115,8 +96,24 @@ class APISupervisor(CoreSysAttributes):
ATTR_DEBUG: self.sys_config.debug, ATTR_DEBUG: self.sys_config.debug,
ATTR_DEBUG_BLOCK: self.sys_config.debug_block, ATTR_DEBUG_BLOCK: self.sys_config.debug_block,
ATTR_DIAGNOSTICS: self.sys_config.diagnostics, ATTR_DIAGNOSTICS: self.sys_config.diagnostics,
ATTR_ADDONS: list_addons, # Depricated
ATTR_ADDONS_REPOSITORIES: self.sys_store.repository_urls, ATTR_ADDONS: [
{
ATTR_NAME: addon.name,
ATTR_SLUG: addon.slug,
ATTR_VERSION: addon.version,
ATTR_VERSION_LATEST: addon.latest_version,
ATTR_UPDATE_AVAILABLE: addon.need_update,
ATTR_STATE: addon.state,
ATTR_REPOSITORY: addon.repository,
ATTR_ICON: addon.with_icon,
}
for addon in self.sys_addons.local.values()
],
ATTR_ADDONS_REPOSITORIES: [
{ATTR_NAME: store.name, ATTR_SLUG: store.slug}
for store in self.sys_store.all
],
} }
@api_process @api_process
@ -146,18 +143,11 @@ class APISupervisor(CoreSysAttributes):
if ATTR_LOGGING in body: if ATTR_LOGGING in body:
self.sys_config.logging = body[ATTR_LOGGING] self.sys_config.logging = body[ATTR_LOGGING]
# REMOVE: 2021.7
if ATTR_CONTENT_TRUST in body:
self.sys_security.content_trust = body[ATTR_CONTENT_TRUST]
# REMOVE: 2021.7
if ATTR_FORCE_SECURITY in body:
self.sys_security.force = body[ATTR_FORCE_SECURITY]
# Save changes before processing addons in case of errors # Save changes before processing addons in case of errors
self.sys_updater.save_data() self.sys_updater.save_data()
self.sys_config.save_data() self.sys_config.save_data()
# Remove: 2022.9
if ATTR_ADDONS_REPOSITORIES in body: if ATTR_ADDONS_REPOSITORIES in body:
await asyncio.shield( await asyncio.shield(
self.sys_store.update_repositories(set(body[ATTR_ADDONS_REPOSITORIES])) self.sys_store.update_repositories(set(body[ATTR_ADDONS_REPOSITORIES]))

View File

@ -10,9 +10,6 @@ import voluptuous as vol
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
from ..const import ( from ..const import (
CONTENT_TYPE_BINARY,
HEADER_TOKEN,
HEADER_TOKEN_OLD,
JSON_DATA, JSON_DATA,
JSON_MESSAGE, JSON_MESSAGE,
JSON_RESULT, JSON_RESULT,
@ -25,22 +22,20 @@ from ..exceptions import APIError, APIForbidden, DockerAPIError, HassioError
from ..utils import check_exception_chain, get_message_from_exception_chain from ..utils import check_exception_chain, get_message_from_exception_chain
from ..utils.json import JSONEncoder from ..utils.json import JSONEncoder
from ..utils.log_format import format_message from ..utils.log_format import format_message
from .const import CONTENT_TYPE_BINARY, HEADER_TOKEN, HEADER_TOKEN_OLD
def excract_supervisor_token(request: web.Request) -> Optional[str]: def excract_supervisor_token(request: web.Request) -> Optional[str]:
"""Extract Supervisor token from request.""" """Extract Supervisor token from request."""
supervisor_token = request.headers.get(HEADER_TOKEN) if supervisor_token := request.headers.get(HEADER_TOKEN):
if supervisor_token:
return supervisor_token return supervisor_token
# Remove with old Supervisor fallback # Old Supervisor fallback
supervisor_token = request.headers.get(HEADER_TOKEN_OLD) if supervisor_token := request.headers.get(HEADER_TOKEN_OLD):
if supervisor_token:
return supervisor_token return supervisor_token
# API access only # API access only
supervisor_token = request.headers.get(AUTHORIZATION) if supervisor_token := request.headers.get(AUTHORIZATION):
if supervisor_token:
return supervisor_token.split(" ")[-1] return supervisor_token.split(" ")[-1]
return None return None

View File

@ -68,21 +68,6 @@ JSON_RESULT = "result"
RESULT_ERROR = "error" RESULT_ERROR = "error"
RESULT_OK = "ok" RESULT_OK = "ok"
CONTENT_TYPE_BINARY = "application/octet-stream"
CONTENT_TYPE_JSON = "application/json"
CONTENT_TYPE_PNG = "image/png"
CONTENT_TYPE_TAR = "application/tar"
CONTENT_TYPE_TEXT = "text/plain"
CONTENT_TYPE_URL = "application/x-www-form-urlencoded"
COOKIE_INGRESS = "ingress_session"
HEADER_TOKEN = "X-Supervisor-Token"
HEADER_TOKEN_OLD = "X-Hassio-Key"
ENV_TIME = "TZ"
ENV_TOKEN = "SUPERVISOR_TOKEN"
ENV_TOKEN_HASSIO = "HASSIO_TOKEN"
ENV_HOMEASSISTANT_REPOSITORY = "HOMEASSISTANT_REPOSITORY" ENV_HOMEASSISTANT_REPOSITORY = "HOMEASSISTANT_REPOSITORY"
ENV_SUPERVISOR_DEV = "SUPERVISOR_DEV" ENV_SUPERVISOR_DEV = "SUPERVISOR_DEV"
ENV_SUPERVISOR_MACHINE = "SUPERVISOR_MACHINE" ENV_SUPERVISOR_MACHINE = "SUPERVISOR_MACHINE"

View File

@ -97,7 +97,6 @@ DBUS_ATTR_LAST_ERROR = "LastError"
DBUS_ATTR_LLMNR = "LLMNR" DBUS_ATTR_LLMNR = "LLMNR"
DBUS_ATTR_LLMNR_HOSTNAME = "LLMNRHostname" DBUS_ATTR_LLMNR_HOSTNAME = "LLMNRHostname"
DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic" DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic"
DBUS_ATTR_LOCALRTC = "LocalRTC"
DBUS_ATTR_MANAGED = "Managed" DBUS_ATTR_MANAGED = "Managed"
DBUS_ATTR_MODE = "Mode" DBUS_ATTR_MODE = "Mode"
DBUS_ATTR_MULTICAST_DNS = "MulticastDNS" DBUS_ATTR_MULTICAST_DNS = "MulticastDNS"

View File

@ -7,7 +7,6 @@ from ..exceptions import DBusError, DBusInterfaceError
from ..utils.dbus import DBus from ..utils.dbus import DBus
from ..utils.dt import utc_from_timestamp from ..utils.dt import utc_from_timestamp
from .const import ( from .const import (
DBUS_ATTR_LOCALRTC,
DBUS_ATTR_NTP, DBUS_ATTR_NTP,
DBUS_ATTR_NTPSYNCHRONIZED, DBUS_ATTR_NTPSYNCHRONIZED,
DBUS_ATTR_TIMEUSEC, DBUS_ATTR_TIMEUSEC,
@ -37,12 +36,6 @@ class TimeDate(DBusInterface):
"""Return host timezone.""" """Return host timezone."""
return self.properties[DBUS_ATTR_TIMEZONE] return self.properties[DBUS_ATTR_TIMEZONE]
@property
@dbus_property
def local_rtc(self) -> bool:
"""Return if a local RTC exists."""
return self.properties[DBUS_ATTR_LOCALRTC]
@property @property
@dbus_property @dbus_property
def ntp(self) -> bool: def ntp(self) -> bool:

Some files were not shown because too many files have changed in this diff Show More