mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-28 03:26:32 +00:00
Merge branch 'main' of https://github.com/home-assistant/supervisor into dark_icon-suport
This commit is contained in:
commit
08af296c86
4
.github/workflows/builder.yml
vendored
4
.github/workflows/builder.yml
vendored
@ -128,7 +128,7 @@ jobs:
|
||||
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
|
||||
|
||||
- name: Build supervisor
|
||||
uses: home-assistant/builder@2022.03.1
|
||||
uses: home-assistant/builder@2022.06.2
|
||||
with:
|
||||
args: |
|
||||
$BUILD_ARGS \
|
||||
@ -213,7 +213,7 @@ jobs:
|
||||
|
||||
- name: Build the Supervisor
|
||||
if: needs.init.outputs.publish != 'true'
|
||||
uses: home-assistant/builder@2022.03.1
|
||||
uses: home-assistant/builder@2022.06.2
|
||||
with:
|
||||
args: |
|
||||
--test \
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 4ad49ef07f65431d047094dda7eb6359fe4d8809
|
||||
Subproject commit e7848262ea94af35a5333ce704385da511701240
|
5
pylintrc
5
pylintrc
@ -12,24 +12,19 @@ extension-pkg-whitelist=
|
||||
# locally-disabled - it spams too much
|
||||
# duplicate-code - unavoidable
|
||||
# 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
|
||||
# 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-few-* - same as too-many-*
|
||||
# abstract-method - with intro of async there are always methods missing
|
||||
disable=
|
||||
format,
|
||||
abstract-class-little-used,
|
||||
abstract-method,
|
||||
cyclic-import,
|
||||
duplicate-code,
|
||||
locally-disabled,
|
||||
no-else-return,
|
||||
no-self-use,
|
||||
not-context-manager,
|
||||
redefined-variable-type,
|
||||
too-few-public-methods,
|
||||
too-many-arguments,
|
||||
too-many-branches,
|
||||
|
@ -5,11 +5,11 @@ flake8-docstrings==1.6.0
|
||||
flake8==4.0.1
|
||||
pre-commit==2.19.0
|
||||
pydocstyle==6.1.1
|
||||
pylint==2.13.9
|
||||
pylint==2.14.3
|
||||
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-cov==3.0.0
|
||||
pytest-timeout==2.1.0
|
||||
pytest==7.1.2
|
||||
pyupgrade==2.32.1
|
||||
pyupgrade==2.34.0
|
||||
time-machine==2.7.0
|
||||
|
@ -84,8 +84,6 @@ RE_WATCHDOG = re.compile(
|
||||
r":\/\/\[HOST\]:(?:\[PORT:)?(?P<t_port>\d+)\]?(?P<s_suffix>.*)$"
|
||||
)
|
||||
|
||||
RE_OLD_AUDIO = re.compile(r"\d+,\d+")
|
||||
|
||||
WATCHDOG_TIMEOUT = aiohttp.ClientTimeout(total=10)
|
||||
|
||||
_OPTIONS_MERGER: Final = Merger(
|
||||
@ -367,13 +365,7 @@ class Addon(AddonModel):
|
||||
"""Return a pulse profile for output or None."""
|
||||
if not self.with_audio:
|
||||
return None
|
||||
|
||||
# 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
|
||||
return self.persist.get(ATTR_AUDIO_OUTPUT)
|
||||
|
||||
@audio_output.setter
|
||||
def audio_output(self, value: Optional[str]):
|
||||
@ -386,12 +378,7 @@ class Addon(AddonModel):
|
||||
if not self.with_audio:
|
||||
return None
|
||||
|
||||
# Fallback with old audio settings
|
||||
# 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
|
||||
return self.persist.get(ATTR_AUDIO_INPUT)
|
||||
|
||||
@audio_input.setter
|
||||
def audio_input(self, value: Optional[str]) -> None:
|
||||
|
@ -240,7 +240,7 @@ class RestAPI(CoreSysAttributes):
|
||||
[web.get("/available_updates", api_root.available_updates)]
|
||||
)
|
||||
|
||||
# Remove 2023
|
||||
# Remove: 2023
|
||||
self.webapp.add_routes(
|
||||
[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/check", api_hass.check),
|
||||
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/logs", api_hass.logs),
|
||||
web.get("/homeassistant/stats", api_hass.stats),
|
||||
web.post("/homeassistant/options", api_hass.options),
|
||||
web.post("/homeassistant/update", api_hass.update),
|
||||
web.post("/homeassistant/restart", api_hass.restart),
|
||||
web.post("/homeassistant/stop", api_hass.stop),
|
||||
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/check", api_hass.check),
|
||||
]
|
||||
)
|
||||
|
||||
@ -350,7 +355,12 @@ class RestAPI(CoreSysAttributes):
|
||||
web.post("/core/api/{path:.+}", api_proxy.api),
|
||||
web.get("/core/api/{path:.+}", 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/websocket", api_proxy.websocket),
|
||||
web.get("/homeassistant/api/stream", api_proxy.stream),
|
||||
@ -368,8 +378,10 @@ class RestAPI(CoreSysAttributes):
|
||||
self.webapp.add_routes(
|
||||
[
|
||||
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}/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}/start", api_addons.start),
|
||||
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.post("/addons/{addon}/rebuild", api_addons.rebuild),
|
||||
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}/security", api_addons.security),
|
||||
web.get("/addons/{addon}/stats", api_addons.stats),
|
||||
@ -414,21 +420,6 @@ class RestAPI(CoreSysAttributes):
|
||||
|
||||
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.post("/backups/reload", api_backups.reload),
|
||||
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/{addon}", 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(
|
||||
"/store/addons/{addon}/install", api_store.addons_addon_install
|
||||
),
|
||||
@ -551,8 +551,16 @@ class RestAPI(CoreSysAttributes):
|
||||
# Reroute from legacy
|
||||
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}/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,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
@ -52,13 +52,11 @@ from ..const import (
|
||||
ATTR_INGRESS_PANEL,
|
||||
ATTR_INGRESS_PORT,
|
||||
ATTR_INGRESS_URL,
|
||||
ATTR_INSTALLED,
|
||||
ATTR_IP_ADDRESS,
|
||||
ATTR_KERNEL_MODULES,
|
||||
ATTR_LOGO,
|
||||
ATTR_LONG_DESCRIPTION,
|
||||
ATTR_MACHINE,
|
||||
ATTR_MAINTAINER,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
ATTR_MEMORY_USAGE,
|
||||
@ -73,12 +71,10 @@ from ..const import (
|
||||
ATTR_PROTECTED,
|
||||
ATTR_PWNED,
|
||||
ATTR_RATING,
|
||||
ATTR_REPOSITORIES,
|
||||
ATTR_REPOSITORY,
|
||||
ATTR_SCHEMA,
|
||||
ATTR_SERVICES,
|
||||
ATTR_SLUG,
|
||||
ATTR_SOURCE,
|
||||
ATTR_STAGE,
|
||||
ATTR_STARTUP,
|
||||
ATTR_STATE,
|
||||
@ -95,18 +91,14 @@ from ..const import (
|
||||
ATTR_VIDEO,
|
||||
ATTR_WATCHDOG,
|
||||
ATTR_WEBUI,
|
||||
CONTENT_TYPE_BINARY,
|
||||
CONTENT_TYPE_PNG,
|
||||
CONTENT_TYPE_TEXT,
|
||||
REQUEST_FROM,
|
||||
AddonBoot,
|
||||
AddonState,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..docker.stats import DockerStats
|
||||
from ..exceptions import APIError, APIForbidden, PwnedError, PwnedSecret
|
||||
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
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -133,7 +125,7 @@ SCHEMA_SECURITY = vol.Schema({vol.Optional(ATTR_PROTECTED): vol.Boolean()})
|
||||
class APIAddons(CoreSysAttributes):
|
||||
"""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."""
|
||||
addon_slug: str = request.match_info.get("addon")
|
||||
|
||||
@ -147,13 +139,9 @@ class APIAddons(CoreSysAttributes):
|
||||
addon = self.sys_addons.get(addon_slug)
|
||||
if not addon:
|
||||
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:
|
||||
raise APIError("Addon is not installed")
|
||||
|
||||
return addon
|
||||
|
||||
@api_process
|
||||
@ -166,35 +154,23 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_DESCRIPTON: addon.description,
|
||||
ATTR_ADVANCED: addon.advanced,
|
||||
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_UPDATE_AVAILABLE: addon.need_update
|
||||
if addon.is_installed
|
||||
else False,
|
||||
ATTR_INSTALLED: addon.is_installed,
|
||||
ATTR_UPDATE_AVAILABLE: addon.need_update,
|
||||
ATTR_AVAILABLE: addon.available,
|
||||
ATTR_DETACHED: addon.is_detached,
|
||||
ATTR_HOMEASSISTANT: addon.homeassistant_version,
|
||||
ATTR_STATE: addon.state,
|
||||
ATTR_REPOSITORY: addon.repository,
|
||||
ATTR_BUILD: addon.need_build,
|
||||
ATTR_URL: addon.url,
|
||||
ATTR_ICON: addon.with_icon,
|
||||
ATTR_LOGO: addon.with_logo,
|
||||
}
|
||||
for addon in self.sys_addons.all
|
||||
for addon in self.sys_addons.installed
|
||||
]
|
||||
|
||||
data_repositories = [
|
||||
{
|
||||
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}
|
||||
return {ATTR_ADDONS: data_addons}
|
||||
|
||||
@api_process
|
||||
async def reload(self, request: web.Request) -> None:
|
||||
@ -215,11 +191,8 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_LONG_DESCRIPTION: addon.long_description,
|
||||
ATTR_ADVANCED: addon.advanced,
|
||||
ATTR_STAGE: addon.stage,
|
||||
ATTR_AUTO_UPDATE: None,
|
||||
ATTR_REPOSITORY: addon.repository,
|
||||
ATTR_VERSION: None,
|
||||
ATTR_VERSION_LATEST: addon.latest_version,
|
||||
ATTR_UPDATE_AVAILABLE: False,
|
||||
ATTR_PROTECTED: addon.protected,
|
||||
ATTR_RATING: rating_security(addon),
|
||||
ATTR_BOOT: addon.boot,
|
||||
@ -229,7 +202,6 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_MACHINE: addon.supported_machine,
|
||||
ATTR_HOMEASSISTANT: addon.homeassistant_version,
|
||||
ATTR_URL: addon.url,
|
||||
ATTR_STATE: AddonState.UNKNOWN,
|
||||
ATTR_DETACHED: addon.is_detached,
|
||||
ATTR_AVAILABLE: addon.available,
|
||||
ATTR_BUILD: addon.need_build,
|
||||
@ -242,13 +214,11 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_PRIVILEGED: addon.privileged,
|
||||
ATTR_FULL_ACCESS: addon.with_full_access,
|
||||
ATTR_APPARMOR: addon.apparmor,
|
||||
ATTR_DEVICES: addon.static_devices,
|
||||
ATTR_ICON: addon.with_icon,
|
||||
ATTR_LOGO: addon.with_logo,
|
||||
ATTR_CHANGELOG: addon.with_changelog,
|
||||
ATTR_DOCUMENTATION: addon.with_documentation,
|
||||
ATTR_STDIN: addon.with_stdin,
|
||||
ATTR_WEBUI: None,
|
||||
ATTR_HASSIO_API: addon.access_hassio_api,
|
||||
ATTR_HASSIO_ROLE: addon.hassio_role,
|
||||
ATTR_AUTH_API: addon.access_auth_api,
|
||||
@ -262,49 +232,35 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_DOCKER_API: addon.access_docker_api,
|
||||
ATTR_VIDEO: addon.with_video,
|
||||
ATTR_AUDIO: addon.with_audio,
|
||||
ATTR_AUDIO_INPUT: None,
|
||||
ATTR_AUDIO_OUTPUT: None,
|
||||
ATTR_STARTUP: addon.startup,
|
||||
ATTR_SERVICES: _pretty_services(addon),
|
||||
ATTR_DISCOVERY: addon.discovery,
|
||||
ATTR_IP_ADDRESS: None,
|
||||
ATTR_TRANSLATIONS: addon.translations,
|
||||
ATTR_INGRESS: addon.with_ingress,
|
||||
ATTR_SIGNED: addon.signed,
|
||||
ATTR_INGRESS_ENTRY: None,
|
||||
ATTR_INGRESS_URL: None,
|
||||
ATTR_INGRESS_PORT: None,
|
||||
ATTR_INGRESS_PANEL: None,
|
||||
ATTR_WATCHDOG: None,
|
||||
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],
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@api_process
|
||||
async def options(self, request: web.Request) -> None:
|
||||
"""Store user options for add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
|
||||
# Update secrets for validation
|
||||
await self.sys_homeassistant.secrets.reload()
|
||||
@ -339,7 +295,7 @@ class APIAddons(CoreSysAttributes):
|
||||
@api_process
|
||||
async def options_validate(self, request: web.Request) -> None:
|
||||
"""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}
|
||||
|
||||
options = await request.json(loads=json_loads) or addon.options
|
||||
@ -381,7 +337,7 @@ class APIAddons(CoreSysAttributes):
|
||||
slug: str = request.match_info.get("addon")
|
||||
if slug != "self":
|
||||
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
|
||||
await self.sys_homeassistant.secrets.reload()
|
||||
@ -393,7 +349,7 @@ class APIAddons(CoreSysAttributes):
|
||||
@api_process
|
||||
async def security(self, request: web.Request) -> None:
|
||||
"""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)
|
||||
|
||||
if ATTR_PROTECTED in body:
|
||||
@ -405,7 +361,7 @@ class APIAddons(CoreSysAttributes):
|
||||
@api_process
|
||||
async def stats(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return resource information."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
|
||||
stats: DockerStats = await addon.stats()
|
||||
|
||||
@ -423,37 +379,37 @@ class APIAddons(CoreSysAttributes):
|
||||
@api_process
|
||||
def uninstall(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Uninstall add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
return asyncio.shield(addon.uninstall())
|
||||
|
||||
@api_process
|
||||
def start(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Start add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
return asyncio.shield(addon.start())
|
||||
|
||||
@api_process
|
||||
def stop(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Stop add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
return asyncio.shield(addon.stop())
|
||||
|
||||
@api_process
|
||||
def restart(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Restart add-on."""
|
||||
addon: Addon = self._extract_addon_installed(request)
|
||||
addon: Addon = self._extract_addon(request)
|
||||
return asyncio.shield(addon.restart())
|
||||
|
||||
@api_process
|
||||
def rebuild(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Rebuild local build add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
return asyncio.shield(addon.rebuild())
|
||||
|
||||
@api_process_raw(CONTENT_TYPE_BINARY)
|
||||
def logs(self, request: web.Request) -> Awaitable[bytes]:
|
||||
"""Return logs from add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
return addon.logs()
|
||||
|
||||
@api_process_raw(CONTENT_TYPE_PNG)
|
||||
@ -504,30 +460,10 @@ class APIAddons(CoreSysAttributes):
|
||||
with addon.path_logo.open("rb") as png:
|
||||
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
|
||||
async def stdin(self, request: web.Request) -> None:
|
||||
"""Write to stdin of add-on."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
addon = self._extract_addon(request)
|
||||
if not addon.with_stdin:
|
||||
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))
|
||||
|
||||
|
||||
def _pretty_services(addon: AnyAddon) -> list[str]:
|
||||
def _pretty_services(addon: Addon) -> list[str]:
|
||||
"""Return a simplified services role list."""
|
||||
return [f"{name}:{access}" for name, access in addon.services_role.items()]
|
||||
|
@ -29,12 +29,12 @@ from ..const import (
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_VOLUME,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
from ..host.sound import StreamType
|
||||
from ..validate import version_tag
|
||||
from .const import CONTENT_TYPE_BINARY
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
@ -8,15 +8,10 @@ from aiohttp.web_exceptions import HTTPUnauthorized
|
||||
import voluptuous as vol
|
||||
|
||||
from ..addons.addon import Addon
|
||||
from ..const import (
|
||||
ATTR_PASSWORD,
|
||||
ATTR_USERNAME,
|
||||
CONTENT_TYPE_JSON,
|
||||
CONTENT_TYPE_URL,
|
||||
REQUEST_FROM,
|
||||
)
|
||||
from ..const import ATTR_PASSWORD, ATTR_USERNAME, REQUEST_FROM
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIForbidden
|
||||
from .const import CONTENT_TYPE_JSON, CONTENT_TYPE_URL
|
||||
from .utils import api_process, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
@ -26,17 +26,18 @@ from ..const import (
|
||||
ATTR_SLUG,
|
||||
ATTR_TYPE,
|
||||
ATTR_VERSION,
|
||||
CONTENT_TYPE_TAR,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
from .const import CONTENT_TYPE_TAR
|
||||
from .utils import api_process, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
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]
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
|
@ -1,5 +1,17 @@
|
||||
"""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_AGENT_VERSION = "agent_version"
|
||||
ATTR_AVAILABLE_UPDATES = "available_updates"
|
||||
@ -19,4 +31,9 @@ ATTR_SIGNED = "signed"
|
||||
ATTR_STARTUP_TIME = "startup_time"
|
||||
ATTR_UPDATE_TYPE = "update_type"
|
||||
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"
|
||||
|
@ -21,12 +21,11 @@ from ..const import (
|
||||
ATTR_UPDATE_AVAILABLE,
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
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
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
@ -6,14 +6,15 @@ from aiohttp import web
|
||||
|
||||
from ..const import ATTR_AUDIO, ATTR_DEVICES, ATTR_INPUT, ATTR_NAME, ATTR_OUTPUT
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..hardware.const import (
|
||||
from ..hardware.data import Device
|
||||
from .const import (
|
||||
ATTR_ATTRIBUTES,
|
||||
ATTR_BY_ID,
|
||||
ATTR_CHILDREN,
|
||||
ATTR_DEV_PATH,
|
||||
ATTR_SUBSYSTEM,
|
||||
ATTR_SYSFS,
|
||||
)
|
||||
from ..hardware.data import Device
|
||||
from .utils import api_process
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -28,6 +29,7 @@ def device_struct(device: Device) -> dict[str, Any]:
|
||||
ATTR_SUBSYSTEM: device.subsystem,
|
||||
ATTR_BY_ID: device.by_id,
|
||||
ATTR_ATTRIBUTES: device.attributes,
|
||||
ATTR_CHILDREN: device.children,
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,13 +29,12 @@ from ..const import (
|
||||
ATTR_UPDATE_AVAILABLE,
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_WAIT_BOOT,
|
||||
ATTR_WATCHDOG,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
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
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -48,7 +47,6 @@ SCHEMA_OPTIONS = vol.Schema(
|
||||
vol.Optional(ATTR_PORT): network_port,
|
||||
vol.Optional(ATTR_SSL): 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_AUDIO_OUTPUT): 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_SSL: self.sys_homeassistant.api_ssl,
|
||||
ATTR_WATCHDOG: self.sys_homeassistant.watchdog,
|
||||
ATTR_WAIT_BOOT: self.sys_homeassistant.wait_boot,
|
||||
ATTR_AUDIO_INPUT: self.sys_homeassistant.audio_input,
|
||||
ATTR_AUDIO_OUTPUT: self.sys_homeassistant.audio_output,
|
||||
# Remove end of Q3 2020
|
||||
"last_version": self.sys_homeassistant.latest_version,
|
||||
}
|
||||
|
||||
@api_process
|
||||
@ -108,9 +103,6 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
if ATTR_WATCHDOG in body:
|
||||
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:
|
||||
self.sys_homeassistant.refresh_token = body[ATTR_REFRESH_TOKEN]
|
||||
|
||||
|
@ -22,7 +22,6 @@ from ..const import (
|
||||
ATTR_SERVICES,
|
||||
ATTR_STATE,
|
||||
ATTR_TIMEZONE,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .const import (
|
||||
@ -36,7 +35,7 @@ from .const import (
|
||||
ATTR_LLMNR_HOSTNAME,
|
||||
ATTR_STARTUP_TIME,
|
||||
ATTR_USE_NTP,
|
||||
ATTR_USE_RTC,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
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_SYNCHRONIZED: self.sys_host.info.dt_synchronized,
|
||||
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_BOOT_TIMESTAMP: self.sys_host.info.boot_timestamp,
|
||||
ATTR_BROADCAST_LLMNR: self.sys_host.info.broadcast_llmnr,
|
||||
@ -101,11 +99,7 @@ class APIHost(CoreSysAttributes):
|
||||
@api_process
|
||||
def reload(self, request):
|
||||
"""Reload host data."""
|
||||
return asyncio.shield(
|
||||
asyncio.wait(
|
||||
[self.sys_host.reload(), self.sys_resolution.evaluate.evaluate_system()]
|
||||
)
|
||||
)
|
||||
return asyncio.shield(self.sys_host.reload())
|
||||
|
||||
@api_process
|
||||
async def services(self, request):
|
||||
|
@ -22,11 +22,9 @@ from ..const import (
|
||||
ATTR_PANELS,
|
||||
ATTR_SESSION,
|
||||
ATTR_TITLE,
|
||||
COOKIE_INGRESS,
|
||||
HEADER_TOKEN,
|
||||
HEADER_TOKEN_OLD,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .const import COOKIE_INGRESS, HEADER_TOKEN, HEADER_TOKEN_OLD
|
||||
from .utils import api_process, api_validate, require_home_assistant
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
@ -77,7 +77,6 @@ ADDONS_ROLE_ACCESS = {
|
||||
r"^(?:"
|
||||
r"|/.+/info"
|
||||
r"|/backups.*"
|
||||
r"|/snapshots.*"
|
||||
r")$"
|
||||
),
|
||||
ROLE_MANAGER: re.compile(
|
||||
|
@ -18,11 +18,11 @@ from ..const import (
|
||||
ATTR_UPDATE_AVAILABLE,
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
from ..validate import version_tag
|
||||
from .const import CONTENT_TYPE_BINARY
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
@ -1,14 +1,14 @@
|
||||
|
||||
function loadES5() {
|
||||
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);
|
||||
}
|
||||
if (/.*Version\/(?:11|12)(?:\.\d+)*.*Safari\//.test(navigator.userAgent)) {
|
||||
loadES5();
|
||||
} else {
|
||||
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) {
|
||||
loadES5();
|
||||
}
|
||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/166096ca.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/166096ca.js.gz
Normal file
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/5600c9ce.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/5600c9ce.js.gz
Normal file
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/a9666ad5.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/a9666ad5.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/cb70bdfe.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/cb70bdfe.js.gz
Normal file
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
2
supervisor/api/panel/frontend_es5/entrypoint.f8f83860.js
Normal file
2
supervisor/api/panel/frontend_es5/entrypoint.f8f83860.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/entrypoint.f8f83860.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/entrypoint.f8f83860.js.gz
Normal file
Binary file not shown.
Binary file not shown.
@ -1,3 +1,3 @@
|
||||
{
|
||||
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.073e9cdc.js"
|
||||
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.f8f83860.js"
|
||||
}
|
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
@ -55,4 +55,4 @@
|
||||
--app-header-border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
`}}]}}),i.oi)}}]);
|
||||
//# sourceMappingURL=99edc920.js.map
|
||||
//# sourceMappingURL=5d68c82a.js.map
|
BIN
supervisor/api/panel/frontend_latest/5d68c82a.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/5d68c82a.js.gz
Normal file
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
BIN
supervisor/api/panel/frontend_latest/77d93d88.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/77d93d88.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_latest/77d93d88.js.map
Normal file
1
supervisor/api/panel/frontend_latest/77d93d88.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_latest/7ae7e4b2.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/7ae7e4b2.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_latest/7ae7e4b2.js.map
Normal file
1
supervisor/api/panel/frontend_latest/7ae7e4b2.js.map
Normal file
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
BIN
supervisor/api/panel/frontend_latest/a573532b.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/a573532b.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_latest/a573532b.js.map
Normal file
1
supervisor/api/panel/frontend_latest/a573532b.js.map
Normal file
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
@ -108,4 +108,4 @@
|
||||
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}}}]);
|
||||
//# sourceMappingURL=fe3336e8.js.map
|
||||
//# sourceMappingURL=bf6a06c2.js.map
|
BIN
supervisor/api/panel/frontend_latest/bf6a06c2.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/bf6a06c2.js.gz
Normal file
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
BIN
supervisor/api/panel/frontend_latest/entrypoint.b6cf778b.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/entrypoint.b6cf778b.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
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_latest/faf8e392.js.gz
Normal file
BIN
supervisor/api/panel/frontend_latest/faf8e392.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_latest/faf8e392.js.map
Normal file
1
supervisor/api/panel/frontend_latest/faf8e392.js.map
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -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
@ -8,7 +8,7 @@ import voluptuous as vol
|
||||
from ..addons import AnyAddon
|
||||
from ..addons.utils import rating_security
|
||||
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 (
|
||||
ATTR_ADDONS,
|
||||
ATTR_ADVANCED,
|
||||
@ -53,6 +53,7 @@ from ..exceptions import APIError, APIForbidden
|
||||
from ..store.addon import AddonStore
|
||||
from ..store.repository import Repository
|
||||
from ..store.validate import validate_repository
|
||||
from .const import CONTENT_TYPE_PNG, CONTENT_TYPE_TEXT
|
||||
|
||||
SCHEMA_UPDATE = vol.Schema(
|
||||
{
|
||||
@ -206,6 +207,46 @@ class APIStore(CoreSysAttributes):
|
||||
addon: AddonStore = self._extract_addon(request)
|
||||
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
|
||||
async def repositories_list(self, request: web.Request) -> list[dict[str, Any]]:
|
||||
"""Return all repositories."""
|
||||
|
@ -17,14 +17,12 @@ from ..const import (
|
||||
ATTR_CPU_PERCENT,
|
||||
ATTR_DEBUG,
|
||||
ATTR_DEBUG_BLOCK,
|
||||
ATTR_DESCRIPTON,
|
||||
ATTR_DIAGNOSTICS,
|
||||
ATTR_FORCE_SECURITY,
|
||||
ATTR_HEALTHY,
|
||||
ATTR_ICON,
|
||||
ATTR_IP_ADDRESS,
|
||||
ATTR_LOGGING,
|
||||
ATTR_LOGO,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
ATTR_MEMORY_USAGE,
|
||||
@ -40,7 +38,6 @@ from ..const import (
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
ATTR_WAIT_BOOT,
|
||||
CONTENT_TYPE_BINARY,
|
||||
LogLevel,
|
||||
UpdateChannel,
|
||||
)
|
||||
@ -49,6 +46,7 @@ from ..exceptions import APIError
|
||||
from ..store.validate import repositories
|
||||
from ..utils.validate import validate_timezone
|
||||
from ..validate import version_tag, wait_boot
|
||||
from .const import CONTENT_TYPE_BINARY
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -83,23 +81,6 @@ class APISupervisor(CoreSysAttributes):
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""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 {
|
||||
ATTR_VERSION: self.sys_supervisor.version,
|
||||
ATTR_VERSION_LATEST: self.sys_supervisor.latest_version,
|
||||
@ -115,8 +96,24 @@ class APISupervisor(CoreSysAttributes):
|
||||
ATTR_DEBUG: self.sys_config.debug,
|
||||
ATTR_DEBUG_BLOCK: self.sys_config.debug_block,
|
||||
ATTR_DIAGNOSTICS: self.sys_config.diagnostics,
|
||||
ATTR_ADDONS: list_addons,
|
||||
ATTR_ADDONS_REPOSITORIES: self.sys_store.repository_urls,
|
||||
# Depricated
|
||||
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
|
||||
@ -146,18 +143,11 @@ class APISupervisor(CoreSysAttributes):
|
||||
if ATTR_LOGGING in body:
|
||||
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
|
||||
self.sys_updater.save_data()
|
||||
self.sys_config.save_data()
|
||||
|
||||
# Remove: 2022.9
|
||||
if ATTR_ADDONS_REPOSITORIES in body:
|
||||
await asyncio.shield(
|
||||
self.sys_store.update_repositories(set(body[ATTR_ADDONS_REPOSITORIES]))
|
||||
|
@ -10,9 +10,6 @@ import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
from ..const import (
|
||||
CONTENT_TYPE_BINARY,
|
||||
HEADER_TOKEN,
|
||||
HEADER_TOKEN_OLD,
|
||||
JSON_DATA,
|
||||
JSON_MESSAGE,
|
||||
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.json import JSONEncoder
|
||||
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]:
|
||||
"""Extract Supervisor token from request."""
|
||||
supervisor_token = request.headers.get(HEADER_TOKEN)
|
||||
if supervisor_token:
|
||||
if supervisor_token := request.headers.get(HEADER_TOKEN):
|
||||
return supervisor_token
|
||||
|
||||
# Remove with old Supervisor fallback
|
||||
supervisor_token = request.headers.get(HEADER_TOKEN_OLD)
|
||||
if supervisor_token:
|
||||
# Old Supervisor fallback
|
||||
if supervisor_token := request.headers.get(HEADER_TOKEN_OLD):
|
||||
return supervisor_token
|
||||
|
||||
# API access only
|
||||
supervisor_token = request.headers.get(AUTHORIZATION)
|
||||
if supervisor_token:
|
||||
if supervisor_token := request.headers.get(AUTHORIZATION):
|
||||
return supervisor_token.split(" ")[-1]
|
||||
|
||||
return None
|
||||
|
@ -68,21 +68,6 @@ JSON_RESULT = "result"
|
||||
RESULT_ERROR = "error"
|
||||
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_SUPERVISOR_DEV = "SUPERVISOR_DEV"
|
||||
ENV_SUPERVISOR_MACHINE = "SUPERVISOR_MACHINE"
|
||||
|
@ -97,7 +97,6 @@ DBUS_ATTR_LAST_ERROR = "LastError"
|
||||
DBUS_ATTR_LLMNR = "LLMNR"
|
||||
DBUS_ATTR_LLMNR_HOSTNAME = "LLMNRHostname"
|
||||
DBUS_ATTR_LOADER_TIMESTAMP_MONOTONIC = "LoaderTimestampMonotonic"
|
||||
DBUS_ATTR_LOCALRTC = "LocalRTC"
|
||||
DBUS_ATTR_MANAGED = "Managed"
|
||||
DBUS_ATTR_MODE = "Mode"
|
||||
DBUS_ATTR_MULTICAST_DNS = "MulticastDNS"
|
||||
|
@ -7,7 +7,6 @@ from ..exceptions import DBusError, DBusInterfaceError
|
||||
from ..utils.dbus import DBus
|
||||
from ..utils.dt import utc_from_timestamp
|
||||
from .const import (
|
||||
DBUS_ATTR_LOCALRTC,
|
||||
DBUS_ATTR_NTP,
|
||||
DBUS_ATTR_NTPSYNCHRONIZED,
|
||||
DBUS_ATTR_TIMEUSEC,
|
||||
@ -37,12 +36,6 @@ class TimeDate(DBusInterface):
|
||||
"""Return host 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
|
||||
@dbus_property
|
||||
def ntp(self) -> bool:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user