mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-08-31 11:50:21 +00:00
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8c59e6d05a | ||
![]() |
5c66278a1c | ||
![]() |
7abe9487a0 | ||
![]() |
73832dd6d6 | ||
![]() |
6cc3df54e9 | ||
![]() |
c07c7c5146 | ||
![]() |
a6d1078fe3 | ||
![]() |
eba6da485d | ||
![]() |
de880e24ed | ||
![]() |
f344df9e5c | ||
![]() |
5af62a8834 | ||
![]() |
800fb683f8 | ||
![]() |
ad2566d58a | ||
![]() |
6c679b07e1 | ||
![]() |
aa4f4c8d47 | ||
![]() |
b83da5d89f | ||
![]() |
0afff9a9e2 | ||
![]() |
0433d72ae6 | ||
![]() |
d33beb06cd | ||
![]() |
279d6ccd79 | ||
![]() |
af628293f3 | ||
![]() |
df6b815175 | ||
![]() |
d6127832a7 | ||
![]() |
8240623806 | ||
![]() |
2b4527fa64 | ||
![]() |
23143aede4 | ||
![]() |
8b93f0aee7 | ||
![]() |
5cc4a9a929 | ||
![]() |
288d2e5bdb | ||
![]() |
73d84113ea | ||
![]() |
4b15945ca1 | ||
![]() |
10720b2988 | ||
![]() |
bb991b69bb | ||
![]() |
7c9f6067c0 | ||
![]() |
e960a70217 | ||
![]() |
9b0a2e6da9 | ||
![]() |
cd0c151bd9 | ||
![]() |
b03c8c24dd | ||
![]() |
4416b6524e | ||
![]() |
c9d3f65cc8 | ||
![]() |
0407122fbe | ||
![]() |
5e871d9399 | ||
![]() |
6df7a88666 | ||
![]() |
5933b66b1c | ||
![]() |
a85e816cd7 | ||
![]() |
96f6c07912 | ||
![]() |
40bcee38f3 | ||
![]() |
6d2a38c96e | ||
![]() |
dafc2cfec2 | ||
![]() |
04f36e92e1 | ||
![]() |
4f97013df4 | ||
![]() |
53eae96a98 | ||
![]() |
74530baeb7 | ||
![]() |
271e4f0cc4 | ||
![]() |
f4c7f2cae1 | ||
![]() |
24cdb4787a | ||
![]() |
57b1c21af4 | ||
![]() |
f0eddb6926 | ||
![]() |
7c74c1bd8c | ||
![]() |
2d4a85ae43 | ||
![]() |
d48c439737 | ||
![]() |
874c50d3e8 | ||
![]() |
4beaf571c2 | ||
![]() |
58a948447e | ||
![]() |
32af7ef28b | ||
![]() |
208fb549b7 | ||
![]() |
ab704c11cf | ||
![]() |
966b962ccf | ||
![]() |
4ea3695982 |
27
.github/workflows/builder.yml
vendored
27
.github/workflows/builder.yml
vendored
@@ -119,15 +119,15 @@ jobs:
|
||||
uses: docker/login-action@v1.10.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ secrets.GIT_USER }}
|
||||
password: ${{ secrets.GIT_TOKEN }}
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set build arguments
|
||||
if: needs.init.outputs.publish == 'false'
|
||||
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
|
||||
|
||||
- name: Build supervisor
|
||||
uses: home-assistant/builder@2021.07.0
|
||||
uses: home-assistant/builder@2021.09.0
|
||||
with:
|
||||
args: |
|
||||
$BUILD_ARGS \
|
||||
@@ -192,13 +192,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Run the Supervisor
|
||||
needs: ["build", "codenotary", "init"]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
|
||||
- name: Build the Supervisor
|
||||
if: needs.init.outputs.publish != 'true'
|
||||
uses: home-assistant/builder@2021.07.0
|
||||
uses: home-assistant/builder@2021.09.0
|
||||
with:
|
||||
args: |
|
||||
--test \
|
||||
@@ -209,8 +210,8 @@ jobs:
|
||||
- name: Pull Supervisor
|
||||
if: needs.init.outputs.publish == 'true'
|
||||
run: |
|
||||
docker pull homeassistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }}
|
||||
docker tag homeassistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }} homeassistant/amd64-hassio-supervisor:runner
|
||||
docker pull ghcr.io/home-assistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }}
|
||||
docker tag ghcr.io/home-assistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }} homeassistant/amd64-hassio-supervisor:runner
|
||||
|
||||
- name: Create the Supervisor
|
||||
run: |
|
||||
@@ -218,7 +219,7 @@ jobs:
|
||||
docker create --name hassio_supervisor \
|
||||
--privileged \
|
||||
--security-opt seccomp=unconfined \
|
||||
--security-opt apparmor:unconfined \
|
||||
--security-opt apparmor=unconfined \
|
||||
-v /run/docker.sock:/run/docker.sock \
|
||||
-v /run/dbus:/run/dbus \
|
||||
-v /tmp/supervisor/data:/data \
|
||||
@@ -240,21 +241,18 @@ jobs:
|
||||
ping=$(curl -sSL "http://$SUPERVISOR/supervisor/ping" | jq -r '.result')
|
||||
sleep 5
|
||||
done
|
||||
docker logs hassio_supervisor
|
||||
|
||||
- name: Check the Supervisor
|
||||
run: |
|
||||
echo "Checking supervisor info"
|
||||
test=$(docker exec hassio_cli ha supervisor info --no-progress --raw-json | jq -r '.result')
|
||||
if [ "$test" != "ok" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking supervisor network info"
|
||||
test=$(docker exec hassio_cli ha network info --no-progress --raw-json | jq -r '.result')
|
||||
if [ "$test" != "ok" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -263,14 +261,12 @@ jobs:
|
||||
echo "Install Core SSH Add-on"
|
||||
test=$(docker exec hassio_cli ha addons install core_ssh --no-progress --raw-json | jq -r '.result')
|
||||
if [ "$test" != "ok" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Start Core SSH Add-on"
|
||||
test=$(docker exec hassio_cli ha addons start core_ssh --no-progress --raw-json | jq -r '.result')
|
||||
if [ "$test" != "ok" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -280,20 +276,21 @@ jobs:
|
||||
echo "Enable Content-Trust"
|
||||
test=$(docker exec hassio_cli ha security options --content-trust=true --no-progress --raw-json | jq -r '.result')
|
||||
if [ "$test" != "ok" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Run supervisor health check"
|
||||
test=$(docker exec hassio_cli ha resolution healthcheck --no-progress --raw-json | jq -r '.result')
|
||||
if [ "$test" != "ok" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Check supervisor unhealthy"
|
||||
test=$(docker exec hassio_cli ha resolution info --no-progress --raw-json | jq -r '.data.unhealthy[]')
|
||||
if [ "$test" != "" ];then
|
||||
docker logs hassio_supervisor
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Get supervisor logs on failiure
|
||||
if: ${{ cancelled() || failure() }}
|
||||
run: docker logs hassio_supervisor
|
||||
|
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@@ -357,7 +357,7 @@ jobs:
|
||||
- name: Install VCN tools
|
||||
uses: home-assistant/actions/helpers/vcn@master
|
||||
with:
|
||||
vnc_version: ${{ env.DEFAULT_VCN }}
|
||||
vcn_version: ${{ env.DEFAULT_VCN }}
|
||||
- name: Restore Python virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.6
|
||||
@@ -433,4 +433,4 @@ jobs:
|
||||
coverage report
|
||||
coverage xml
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v2.0.3
|
||||
uses: codecov/codecov-action@v2.1.0
|
||||
|
10
.github/workflows/lock.yml
vendored
10
.github/workflows/lock.yml
vendored
@@ -9,12 +9,12 @@ jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2.1.2
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: "30"
|
||||
issue-exclude-created-before: "2020-10-01T00:00:00Z"
|
||||
issue-inactive-days: "30"
|
||||
exclude-issue-created-before: "2020-10-01T00:00:00Z"
|
||||
issue-lock-reason: ""
|
||||
pr-lock-inactive-days: "1"
|
||||
pr-exclude-created-before: "2020-11-01T00:00:00Z"
|
||||
pr-inactive-days: "1"
|
||||
exclude-pr-created-before: "2020-11-01T00:00:00Z"
|
||||
pr-lock-reason: ""
|
||||
|
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 21.8b0
|
||||
rev: 21.9b0
|
||||
hooks:
|
||||
- id: black
|
||||
args:
|
||||
@@ -28,7 +28,7 @@ repos:
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.6.2
|
||||
rev: v2.29.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
args: [--py39-plus]
|
||||
|
9
.vscode/tasks.json
vendored
9
.vscode/tasks.json
vendored
@@ -32,7 +32,7 @@
|
||||
{
|
||||
"label": "Update Supervisor Panel",
|
||||
"type": "shell",
|
||||
"command": "./scripts/update-frontend.sh",
|
||||
"command": "LOKALISE_TOKEN='${input:localiseToken}' ./scripts/update-frontend.sh",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -86,5 +86,12 @@
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "localiseToken",
|
||||
"type": "promptString",
|
||||
"description": "Paste your lokalise token to download frontend translations"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Submodule home-assistant-polymer updated: 67ab63f00e...910cd98a38
2
pylintrc
2
pylintrc
@@ -2,7 +2,7 @@
|
||||
reports=no
|
||||
jobs=2
|
||||
|
||||
good-names=id,i,j,k,ex,Run,_,fp,T
|
||||
good-names=id,i,j,k,ex,Run,_,fp,T,os
|
||||
|
||||
extension-pkg-whitelist=
|
||||
ciso8601
|
||||
|
@@ -9,12 +9,13 @@ ciso8601==2.2.0
|
||||
colorlog==6.4.1
|
||||
cpe==1.2.1
|
||||
cryptography==3.4.6
|
||||
debugpy==1.4.1
|
||||
docker==5.0.0
|
||||
gitpython==3.1.18
|
||||
debugpy==1.4.3
|
||||
deepmerge==0.3.0
|
||||
docker==5.0.2
|
||||
gitpython==3.1.24
|
||||
jinja2==3.0.1
|
||||
pulsectl==21.5.18
|
||||
pulsectl==21.9.1
|
||||
pyudev==0.22.0
|
||||
ruamel.yaml==0.15.100
|
||||
sentry-sdk==1.3.1
|
||||
voluptuous==0.12.1
|
||||
sentry-sdk==1.4.2
|
||||
voluptuous==0.12.2
|
||||
|
@@ -1,14 +1,14 @@
|
||||
black==21.8b0
|
||||
black==21.9b0
|
||||
codecov==2.1.12
|
||||
coverage==5.5
|
||||
flake8-docstrings==1.6.0
|
||||
flake8==3.9.2
|
||||
pre-commit==2.14.1
|
||||
pre-commit==2.15.0
|
||||
pydocstyle==6.1.1
|
||||
pylint==2.10.2
|
||||
pylint==2.11.1
|
||||
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==2.12.1
|
||||
pytest-timeout==1.4.2
|
||||
pytest==6.2.4
|
||||
pyupgrade==2.25.0
|
||||
pytest==6.2.5
|
||||
pyupgrade==2.29.0
|
||||
|
@@ -15,7 +15,7 @@ function start_docker() {
|
||||
starttime="$(date +%s)"
|
||||
endtime="$(date +%s)"
|
||||
until docker info >/dev/null 2>&1; do
|
||||
if [ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]; then
|
||||
if [[ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]]; then
|
||||
sleep 1
|
||||
endtime=$(date +%s)
|
||||
else
|
||||
@@ -38,7 +38,7 @@ function stop_docker() {
|
||||
# Now wait for it to die
|
||||
kill "$DOCKER_PID"
|
||||
while kill -0 "$DOCKER_PID" 2> /dev/null; do
|
||||
if [ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]; then
|
||||
if [[ $((endtime - starttime)) -le $DOCKER_TIMEOUT ]]; then
|
||||
sleep 1
|
||||
endtime=$(date +%s)
|
||||
else
|
||||
|
@@ -36,7 +36,7 @@ function run_supervisor() {
|
||||
--name hassio_supervisor \
|
||||
--privileged \
|
||||
--security-opt seccomp=unconfined \
|
||||
--security-opt apparmor:unconfined \
|
||||
--security-opt apparmor=unconfined \
|
||||
-v /run/docker.sock:/run/docker.sock:rw \
|
||||
-v /run/dbus:/run/dbus:ro \
|
||||
-v /run/udev:/run/udev:ro \
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#!/bin/bash
|
||||
source "${BASH_SOURCE[0]%/*}/common.sh"
|
||||
|
||||
set -e
|
||||
|
||||
# Update frontend
|
||||
@@ -9,6 +11,10 @@ cd home-assistant-polymer
|
||||
nvm install
|
||||
script/bootstrap
|
||||
|
||||
# Download translations
|
||||
start_docker
|
||||
./script/translations_download
|
||||
|
||||
# build frontend
|
||||
cd hassio
|
||||
./script/build_hassio
|
||||
@@ -16,3 +22,9 @@ cd hassio
|
||||
# Copy frontend
|
||||
rm -rf ../../supervisor/api/panel/*
|
||||
cp -rf build/* ../../supervisor/api/panel/
|
||||
|
||||
# Reset frontend git
|
||||
cd ..
|
||||
git reset --hard HEAD
|
||||
|
||||
stop_docker
|
@@ -3,7 +3,7 @@ import asyncio
|
||||
from contextlib import suppress
|
||||
import logging
|
||||
import tarfile
|
||||
from typing import Dict, List, Optional, Union
|
||||
from typing import Optional, Union
|
||||
|
||||
from ..const import AddonBoot, AddonStartup, AddonState
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
@@ -38,17 +38,17 @@ class AddonManager(CoreSysAttributes):
|
||||
"""Initialize Docker base wrapper."""
|
||||
self.coresys: CoreSys = coresys
|
||||
self.data: AddonsData = AddonsData(coresys)
|
||||
self.local: Dict[str, Addon] = {}
|
||||
self.store: Dict[str, AddonStore] = {}
|
||||
self.local: dict[str, Addon] = {}
|
||||
self.store: dict[str, AddonStore] = {}
|
||||
|
||||
@property
|
||||
def all(self) -> List[AnyAddon]:
|
||||
def all(self) -> list[AnyAddon]:
|
||||
"""Return a list of all add-ons."""
|
||||
addons: Dict[str, AnyAddon] = {**self.store, **self.local}
|
||||
addons: dict[str, AnyAddon] = {**self.store, **self.local}
|
||||
return list(addons.values())
|
||||
|
||||
@property
|
||||
def installed(self) -> List[Addon]:
|
||||
def installed(self) -> list[Addon]:
|
||||
"""Return a list of all installed add-ons."""
|
||||
return list(self.local.values())
|
||||
|
||||
@@ -89,7 +89,7 @@ class AddonManager(CoreSysAttributes):
|
||||
|
||||
async def boot(self, stage: AddonStartup) -> None:
|
||||
"""Boot add-ons with mode auto."""
|
||||
tasks: List[Addon] = []
|
||||
tasks: list[Addon] = []
|
||||
for addon in self.installed:
|
||||
if addon.boot != AddonBoot.AUTO or addon.startup != stage:
|
||||
continue
|
||||
@@ -123,7 +123,7 @@ class AddonManager(CoreSysAttributes):
|
||||
|
||||
async def shutdown(self, stage: AddonStartup) -> None:
|
||||
"""Shutdown addons."""
|
||||
tasks: List[Addon] = []
|
||||
tasks: list[Addon] = []
|
||||
for addon in self.installed:
|
||||
if addon.state != AddonState.STARTED or addon.startup != stage:
|
||||
continue
|
||||
@@ -306,22 +306,24 @@ class AddonManager(CoreSysAttributes):
|
||||
async def rebuild(self, slug: str) -> None:
|
||||
"""Perform a rebuild of local build add-on."""
|
||||
if slug not in self.local:
|
||||
_LOGGER.error("Add-on %s is not installed", slug)
|
||||
raise AddonsError()
|
||||
raise AddonsError(f"Add-on {slug} is not installed", _LOGGER.error)
|
||||
addon = self.local[slug]
|
||||
|
||||
if addon.is_detached:
|
||||
_LOGGER.error("Add-on %s is not available inside store", slug)
|
||||
raise AddonsError()
|
||||
raise AddonsError(
|
||||
f"Add-on {slug} is not available inside store", _LOGGER.error
|
||||
)
|
||||
store = self.store[slug]
|
||||
|
||||
# Check if a rebuild is possible now
|
||||
if addon.version != store.version:
|
||||
_LOGGER.error("Version changed, use Update instead Rebuild")
|
||||
raise AddonsError()
|
||||
raise AddonsError(
|
||||
"Version changed, use Update instead Rebuild", _LOGGER.error
|
||||
)
|
||||
if not addon.need_build:
|
||||
_LOGGER.error("Can't rebuild a image based add-on")
|
||||
raise AddonsNotSupportedError()
|
||||
raise AddonsNotSupportedError(
|
||||
"Can't rebuild a image based add-on", _LOGGER.error
|
||||
)
|
||||
|
||||
# remove docker container but not addon config
|
||||
last_state: AddonState = addon.state
|
||||
@@ -371,7 +373,7 @@ class AddonManager(CoreSysAttributes):
|
||||
@Job(conditions=[JobCondition.FREE_SPACE, JobCondition.INTERNET_HOST])
|
||||
async def repair(self) -> None:
|
||||
"""Repair local add-ons."""
|
||||
needs_repair: List[Addon] = []
|
||||
needs_repair: list[Addon] = []
|
||||
|
||||
# Evaluate Add-ons to repair
|
||||
for addon in self.installed:
|
||||
|
@@ -10,9 +10,10 @@ import secrets
|
||||
import shutil
|
||||
import tarfile
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Any, Awaitable, Dict, List, Optional, Set
|
||||
from typing import Any, Awaitable, Final, Optional
|
||||
|
||||
import aiohttp
|
||||
from deepmerge import Merger
|
||||
import voluptuous as vol
|
||||
from voluptuous.humanize import humanize_error
|
||||
|
||||
@@ -87,6 +88,12 @@ RE_OLD_AUDIO = re.compile(r"\d+,\d+")
|
||||
|
||||
WATCHDOG_TIMEOUT = aiohttp.ClientTimeout(total=10)
|
||||
|
||||
_OPTIONS_MERGER: Final = Merger(
|
||||
type_strategies=[(dict, ["merge"])],
|
||||
fallback_strategies=["override"],
|
||||
type_conflict_strategies=["override"],
|
||||
)
|
||||
|
||||
|
||||
class Addon(AddonModel):
|
||||
"""Hold data for add-on inside Supervisor."""
|
||||
@@ -187,17 +194,19 @@ class Addon(AddonModel):
|
||||
return self.version != self.latest_version
|
||||
|
||||
@property
|
||||
def dns(self) -> List[str]:
|
||||
def dns(self) -> list[str]:
|
||||
"""Return list of DNS name for that add-on."""
|
||||
return [f"{self.hostname}.{DNS_SUFFIX}"]
|
||||
|
||||
@property
|
||||
def options(self) -> Dict[str, Any]:
|
||||
def options(self) -> dict[str, Any]:
|
||||
"""Return options with local changes."""
|
||||
return {**self.data[ATTR_OPTIONS], **self.persist[ATTR_OPTIONS]}
|
||||
return _OPTIONS_MERGER.merge(
|
||||
deepcopy(self.data[ATTR_OPTIONS]), deepcopy(self.persist[ATTR_OPTIONS])
|
||||
)
|
||||
|
||||
@options.setter
|
||||
def options(self, value: Optional[Dict[str, Any]]) -> None:
|
||||
def options(self, value: Optional[dict[str, Any]]) -> None:
|
||||
"""Store user add-on options."""
|
||||
self.persist[ATTR_OPTIONS] = {} if value is None else deepcopy(value)
|
||||
|
||||
@@ -274,12 +283,12 @@ class Addon(AddonModel):
|
||||
self.persist[ATTR_PROTECTED] = value
|
||||
|
||||
@property
|
||||
def ports(self) -> Optional[Dict[str, Optional[int]]]:
|
||||
def ports(self) -> Optional[dict[str, Optional[int]]]:
|
||||
"""Return ports of add-on."""
|
||||
return self.persist.get(ATTR_NETWORK, super().ports)
|
||||
|
||||
@ports.setter
|
||||
def ports(self, value: Optional[Dict[str, Optional[int]]]) -> None:
|
||||
def ports(self, value: Optional[dict[str, Optional[int]]]) -> None:
|
||||
"""Set custom ports of add-on."""
|
||||
if value is None:
|
||||
self.persist.pop(ATTR_NETWORK, None)
|
||||
@@ -425,7 +434,7 @@ class Addon(AddonModel):
|
||||
return Path(self.sys_config.path_extern_tmp, f"{self.slug}_pulse")
|
||||
|
||||
@property
|
||||
def devices(self) -> Set[Device]:
|
||||
def devices(self) -> set[Device]:
|
||||
"""Extract devices from add-on options."""
|
||||
options_schema = self.schema
|
||||
with suppress(vol.Invalid):
|
||||
@@ -434,7 +443,7 @@ class Addon(AddonModel):
|
||||
return options_schema.devices
|
||||
|
||||
@property
|
||||
def pwned(self) -> Set[str]:
|
||||
def pwned(self) -> set[str]:
|
||||
"""Extract pwned data for add-on options."""
|
||||
options_schema = self.schema
|
||||
with suppress(vol.Invalid):
|
||||
@@ -530,8 +539,7 @@ class Addon(AddonModel):
|
||||
|
||||
# Write pulse config
|
||||
try:
|
||||
with self.path_pulse.open("w") as config_file:
|
||||
config_file.write(pulse_config)
|
||||
self.path_pulse.write_text(pulse_config, encoding="utf-8")
|
||||
except OSError as err:
|
||||
_LOGGER.error(
|
||||
"Add-on %s can't write pulse/client.config: %s", self.slug, err
|
||||
@@ -671,8 +679,9 @@ class Addon(AddonModel):
|
||||
Return a coroutine.
|
||||
"""
|
||||
if not self.with_stdin:
|
||||
_LOGGER.error("Add-on %s does not support writing to stdin!", self.slug)
|
||||
raise AddonsNotSupportedError()
|
||||
raise AddonsNotSupportedError(
|
||||
f"Add-on {self.slug} does not support writing to stdin!", _LOGGER.error
|
||||
)
|
||||
|
||||
try:
|
||||
return await self.instance.write_stdin(data)
|
||||
@@ -719,8 +728,9 @@ class Addon(AddonModel):
|
||||
try:
|
||||
write_json_file(temp_path.joinpath("addon.json"), data)
|
||||
except ConfigurationFileError as err:
|
||||
_LOGGER.error("Can't save meta for %s", self.slug)
|
||||
raise AddonsError() from err
|
||||
raise AddonsError(
|
||||
f"Can't save meta for {self.slug}", _LOGGER.error
|
||||
) from err
|
||||
|
||||
# Store AppArmor Profile
|
||||
if self.sys_host.apparmor.exists(self.slug):
|
||||
@@ -728,8 +738,9 @@ class Addon(AddonModel):
|
||||
try:
|
||||
self.sys_host.apparmor.backup_profile(self.slug, profile)
|
||||
except HostAppArmorError as err:
|
||||
_LOGGER.error("Can't backup AppArmor profile")
|
||||
raise AddonsError() from err
|
||||
raise AddonsError(
|
||||
"Can't backup AppArmor profile", _LOGGER.error
|
||||
) from err
|
||||
|
||||
# write into tarfile
|
||||
def _write_tarfile():
|
||||
@@ -761,8 +772,9 @@ class Addon(AddonModel):
|
||||
_LOGGER.info("Building backup for add-on %s", self.slug)
|
||||
await self.sys_run_in_executor(_write_tarfile)
|
||||
except (tarfile.TarError, OSError) as err:
|
||||
_LOGGER.error("Can't write tarfile %s: %s", tar_file, err)
|
||||
raise AddonsError() from err
|
||||
raise AddonsError(
|
||||
f"Can't write tarfile {tar_file}: {err}", _LOGGER.error
|
||||
) from err
|
||||
finally:
|
||||
if (
|
||||
is_running
|
||||
@@ -788,8 +800,9 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.sys_run_in_executor(_extract_tarfile)
|
||||
except tarfile.TarError as err:
|
||||
_LOGGER.error("Can't read tarfile %s: %s", tar_file, err)
|
||||
raise AddonsError() from err
|
||||
raise AddonsError(
|
||||
f"Can't read tarfile {tar_file}: {err}", _LOGGER.error
|
||||
) from err
|
||||
|
||||
# Read backup data
|
||||
try:
|
||||
@@ -810,8 +823,10 @@ class Addon(AddonModel):
|
||||
|
||||
# If available
|
||||
if not self._available(data[ATTR_SYSTEM]):
|
||||
_LOGGER.error("Add-on %s is not available for this platform", self.slug)
|
||||
raise AddonsNotSupportedError()
|
||||
raise AddonsNotSupportedError(
|
||||
f"Add-on {self.slug} is not available for this platform",
|
||||
_LOGGER.error,
|
||||
)
|
||||
|
||||
# Restore local add-on information
|
||||
_LOGGER.info("Restore config for addon %s", self.slug)
|
||||
@@ -852,8 +867,9 @@ class Addon(AddonModel):
|
||||
try:
|
||||
await self.sys_run_in_executor(_restore_data)
|
||||
except shutil.Error as err:
|
||||
_LOGGER.error("Can't restore origin data: %s", err)
|
||||
raise AddonsError() from err
|
||||
raise AddonsError(
|
||||
f"Can't restore origin data: {err}", _LOGGER.error
|
||||
) from err
|
||||
|
||||
# Restore AppArmor
|
||||
profile_file = Path(temp, "apparmor.txt")
|
||||
|
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Dict
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from awesomeversion import AwesomeVersion
|
||||
|
||||
@@ -60,12 +60,12 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
|
||||
return self._data[ATTR_SQUASH]
|
||||
|
||||
@property
|
||||
def additional_args(self) -> Dict[str, str]:
|
||||
def additional_args(self) -> dict[str, str]:
|
||||
"""Return additional Docker build arguments."""
|
||||
return self._data[ATTR_ARGS]
|
||||
|
||||
@property
|
||||
def additional_labels(self) -> Dict[str, str]:
|
||||
def additional_labels(self) -> dict[str, str]:
|
||||
"""Return additional Docker labels."""
|
||||
return self._data[ATTR_LABELS]
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Init file for Supervisor add-on data."""
|
||||
from copy import deepcopy
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from ..const import (
|
||||
ATTR_IMAGE,
|
||||
@@ -16,7 +16,7 @@ from ..utils.common import FileConfiguration
|
||||
from .addon import Addon
|
||||
from .validate import SCHEMA_ADDONS_FILE
|
||||
|
||||
Config = Dict[str, Any]
|
||||
Config = dict[str, Any]
|
||||
|
||||
|
||||
class AddonsData(FileConfiguration, CoreSysAttributes):
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor add-ons."""
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Any, Awaitable, Dict, List, Optional
|
||||
from typing import Any, Awaitable, Optional
|
||||
|
||||
from awesomeversion import AwesomeVersion, AwesomeVersionException
|
||||
|
||||
@@ -83,7 +83,7 @@ from .const import ATTR_BACKUP
|
||||
from .options import AddonOptions, UiOptions
|
||||
from .validate import RE_SERVICE, RE_VOLUME
|
||||
|
||||
Data = Dict[str, Any]
|
||||
Data = dict[str, Any]
|
||||
|
||||
|
||||
class AddonModel(CoreSysAttributes, ABC):
|
||||
@@ -115,7 +115,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return self._available(self.data)
|
||||
|
||||
@property
|
||||
def options(self) -> Dict[str, Any]:
|
||||
def options(self) -> dict[str, Any]:
|
||||
"""Return options with local changes."""
|
||||
return self.data[ATTR_OPTIONS]
|
||||
|
||||
@@ -140,7 +140,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return self.slug.replace("_", "-")
|
||||
|
||||
@property
|
||||
def dns(self) -> List[str]:
|
||||
def dns(self) -> list[str]:
|
||||
"""Return list of DNS name for that add-on."""
|
||||
return []
|
||||
|
||||
@@ -184,8 +184,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return None
|
||||
|
||||
# Return data
|
||||
with readme.open("r") as readme_file:
|
||||
return readme_file.read()
|
||||
return readme.read_text(encoding="utf-8")
|
||||
|
||||
@property
|
||||
def repository(self) -> str:
|
||||
@@ -228,7 +227,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return self.data[ATTR_STAGE]
|
||||
|
||||
@property
|
||||
def services_role(self) -> Dict[str, str]:
|
||||
def services_role(self) -> dict[str, str]:
|
||||
"""Return dict of services with rights."""
|
||||
services_list = self.data.get(ATTR_SERVICES, [])
|
||||
|
||||
@@ -241,17 +240,17 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return services
|
||||
|
||||
@property
|
||||
def discovery(self) -> List[str]:
|
||||
def discovery(self) -> list[str]:
|
||||
"""Return list of discoverable components/platforms."""
|
||||
return self.data.get(ATTR_DISCOVERY, [])
|
||||
|
||||
@property
|
||||
def ports_description(self) -> Optional[Dict[str, str]]:
|
||||
def ports_description(self) -> Optional[dict[str, str]]:
|
||||
"""Return descriptions of ports."""
|
||||
return self.data.get(ATTR_PORTS_DESCRIPTION)
|
||||
|
||||
@property
|
||||
def ports(self) -> Optional[Dict[str, Optional[int]]]:
|
||||
def ports(self) -> Optional[dict[str, Optional[int]]]:
|
||||
"""Return ports of add-on."""
|
||||
return self.data.get(ATTR_PORTS)
|
||||
|
||||
@@ -311,17 +310,17 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return self.data[ATTR_HOST_DBUS]
|
||||
|
||||
@property
|
||||
def static_devices(self) -> List[Path]:
|
||||
def static_devices(self) -> list[Path]:
|
||||
"""Return static devices of add-on."""
|
||||
return [Path(node) for node in self.data.get(ATTR_DEVICES, [])]
|
||||
|
||||
@property
|
||||
def environment(self) -> Optional[Dict[str, str]]:
|
||||
def environment(self) -> Optional[dict[str, str]]:
|
||||
"""Return environment of add-on."""
|
||||
return self.data.get(ATTR_ENVIRONMENT)
|
||||
|
||||
@property
|
||||
def privileged(self) -> List[Capabilities]:
|
||||
def privileged(self) -> list[Capabilities]:
|
||||
"""Return list of privilege."""
|
||||
return self.data.get(ATTR_PRIVILEGED, [])
|
||||
|
||||
@@ -360,7 +359,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return self.data[ATTR_HASSIO_ROLE]
|
||||
|
||||
@property
|
||||
def backup_exclude(self) -> List[str]:
|
||||
def backup_exclude(self) -> list[str]:
|
||||
"""Return Exclude list for backup."""
|
||||
return self.data.get(ATTR_BACKUP_EXCLUDE, [])
|
||||
|
||||
@@ -495,12 +494,12 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return self.path_documentation.exists()
|
||||
|
||||
@property
|
||||
def supported_arch(self) -> List[str]:
|
||||
def supported_arch(self) -> list[str]:
|
||||
"""Return list of supported arch."""
|
||||
return self.data[ATTR_ARCH]
|
||||
|
||||
@property
|
||||
def supported_machine(self) -> List[str]:
|
||||
def supported_machine(self) -> list[str]:
|
||||
"""Return list of supported machine."""
|
||||
return self.data.get(ATTR_MACHINE, [])
|
||||
|
||||
@@ -515,7 +514,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return ATTR_IMAGE not in self.data
|
||||
|
||||
@property
|
||||
def map_volumes(self) -> Dict[str, str]:
|
||||
def map_volumes(self) -> dict[str, str]:
|
||||
"""Return a dict of {volume: policy} from add-on."""
|
||||
volumes = {}
|
||||
for volume in self.data[ATTR_MAP]:
|
||||
@@ -566,7 +565,7 @@ class AddonModel(CoreSysAttributes, ABC):
|
||||
return AddonOptions(self.coresys, raw_schema, self.name, self.slug)
|
||||
|
||||
@property
|
||||
def schema_ui(self) -> Optional[List[Dict[any, any]]]:
|
||||
def schema_ui(self) -> Optional[list[dict[any, any]]]:
|
||||
"""Create a UI schema for add-on options."""
|
||||
raw_schema = self.data[ATTR_SCHEMA]
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import hashlib
|
||||
import logging
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import Any, Dict, List, Set, Union
|
||||
from typing import Any, Union
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -59,13 +59,13 @@ class AddonOptions(CoreSysAttributes):
|
||||
"""Validate Add-ons Options."""
|
||||
|
||||
def __init__(
|
||||
self, coresys: CoreSys, raw_schema: Dict[str, Any], name: str, slug: str
|
||||
self, coresys: CoreSys, raw_schema: dict[str, Any], name: str, slug: str
|
||||
):
|
||||
"""Validate schema."""
|
||||
self.coresys: CoreSys = coresys
|
||||
self.raw_schema: Dict[str, Any] = raw_schema
|
||||
self.devices: Set[Device] = set()
|
||||
self.pwned: Set[str] = set()
|
||||
self.raw_schema: dict[str, Any] = raw_schema
|
||||
self.devices: set[Device] = set()
|
||||
self.pwned: set[str] = set()
|
||||
self._name = name
|
||||
self._slug = slug
|
||||
|
||||
@@ -187,7 +187,7 @@ class AddonOptions(CoreSysAttributes):
|
||||
f"Fatal error for option '{key}' with type '{typ}' in {self._name} ({self._slug})"
|
||||
) from None
|
||||
|
||||
def _nested_validate_list(self, typ: Any, data_list: List[Any], key: str):
|
||||
def _nested_validate_list(self, typ: Any, data_list: list[Any], key: str):
|
||||
"""Validate nested items."""
|
||||
options = []
|
||||
|
||||
@@ -209,7 +209,7 @@ class AddonOptions(CoreSysAttributes):
|
||||
return options
|
||||
|
||||
def _nested_validate_dict(
|
||||
self, typ: Dict[Any, Any], data_dict: Dict[Any, Any], key: str
|
||||
self, typ: dict[Any, Any], data_dict: dict[Any, Any], key: str
|
||||
):
|
||||
"""Validate nested items."""
|
||||
options = {}
|
||||
@@ -241,7 +241,7 @@ class AddonOptions(CoreSysAttributes):
|
||||
return options
|
||||
|
||||
def _check_missing_options(
|
||||
self, origin: Dict[Any, Any], exists: Dict[Any, Any], root: str
|
||||
self, origin: dict[Any, Any], exists: dict[Any, Any], root: str
|
||||
) -> None:
|
||||
"""Check if all options are exists."""
|
||||
missing = set(origin) - set(exists)
|
||||
@@ -267,9 +267,9 @@ class UiOptions(CoreSysAttributes):
|
||||
"""Initialize UI option render."""
|
||||
self.coresys = coresys
|
||||
|
||||
def __call__(self, raw_schema: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||
def __call__(self, raw_schema: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""Generate UI schema."""
|
||||
ui_schema: List[Dict[str, Any]] = []
|
||||
ui_schema: list[dict[str, Any]] = []
|
||||
|
||||
# read options
|
||||
for key, value in raw_schema.items():
|
||||
@@ -287,13 +287,13 @@ class UiOptions(CoreSysAttributes):
|
||||
|
||||
def _single_ui_option(
|
||||
self,
|
||||
ui_schema: List[Dict[str, Any]],
|
||||
ui_schema: list[dict[str, Any]],
|
||||
value: str,
|
||||
key: str,
|
||||
multiple: bool = False,
|
||||
) -> None:
|
||||
"""Validate a single element."""
|
||||
ui_node: Dict[str, Union[str, bool, float, List[str]]] = {"name": key}
|
||||
ui_node: dict[str, Union[str, bool, float, list[str]]] = {"name": key}
|
||||
|
||||
# If multiple
|
||||
if multiple:
|
||||
@@ -365,8 +365,8 @@ class UiOptions(CoreSysAttributes):
|
||||
|
||||
def _nested_ui_list(
|
||||
self,
|
||||
ui_schema: List[Dict[str, Any]],
|
||||
option_list: List[Any],
|
||||
ui_schema: list[dict[str, Any]],
|
||||
option_list: list[Any],
|
||||
key: str,
|
||||
) -> None:
|
||||
"""UI nested list items."""
|
||||
@@ -383,8 +383,8 @@ class UiOptions(CoreSysAttributes):
|
||||
|
||||
def _nested_ui_dict(
|
||||
self,
|
||||
ui_schema: List[Dict[str, Any]],
|
||||
option_dict: Dict[str, Any],
|
||||
ui_schema: list[dict[str, Any]],
|
||||
option_dict: dict[str, Any],
|
||||
key: str,
|
||||
multiple: bool = False,
|
||||
) -> None:
|
||||
@@ -408,7 +408,7 @@ class UiOptions(CoreSysAttributes):
|
||||
ui_schema.append(ui_node)
|
||||
|
||||
|
||||
def _create_device_filter(str_filter: str) -> Dict[str, Any]:
|
||||
def _create_device_filter(str_filter: str) -> dict[str, Any]:
|
||||
"""Generate device Filter."""
|
||||
raw_filter = dict(value.split("=") for value in str_filter.split(";"))
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import logging
|
||||
import re
|
||||
import secrets
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
import uuid
|
||||
|
||||
import voluptuous as vol
|
||||
@@ -148,7 +148,7 @@ RE_MACHINE = re.compile(
|
||||
)
|
||||
|
||||
|
||||
def _warn_addon_config(config: Dict[str, Any]):
|
||||
def _warn_addon_config(config: dict[str, Any]):
|
||||
"""Warn about miss configs."""
|
||||
name = config.get(ATTR_NAME)
|
||||
if not name:
|
||||
@@ -179,7 +179,7 @@ def _warn_addon_config(config: Dict[str, Any]):
|
||||
def _migrate_addon_config(protocol=False):
|
||||
"""Migrate addon config."""
|
||||
|
||||
def _migrate(config: Dict[str, Any]):
|
||||
def _migrate(config: dict[str, Any]):
|
||||
name = config.get(ATTR_NAME)
|
||||
if not name:
|
||||
raise vol.Invalid("Invalid Add-on config!")
|
||||
@@ -281,7 +281,7 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
|
||||
vol.Optional(ATTR_PANEL_ICON, default="mdi:puzzle"): str,
|
||||
vol.Optional(ATTR_PANEL_TITLE): str,
|
||||
vol.Optional(ATTR_PANEL_ADMIN, default=True): vol.Boolean(),
|
||||
vol.Optional(ATTR_HOMEASSISTANT): vol.Maybe(version_tag),
|
||||
vol.Optional(ATTR_HOMEASSISTANT): version_tag,
|
||||
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
||||
vol.Optional(ATTR_HOST_PID, default=False): vol.Boolean(),
|
||||
vol.Optional(ATTR_HOST_IPC, default=False): vol.Boolean(),
|
||||
|
@@ -145,6 +145,8 @@ class RestAPI(CoreSysAttributes):
|
||||
web.get("/os/info", api_os.info),
|
||||
web.post("/os/update", api_os.update),
|
||||
web.post("/os/config/sync", api_os.config_sync),
|
||||
web.post("/os/datadisk/move", api_os.migrate_data),
|
||||
web.get("/os/datadisk/list", api_os.list_data),
|
||||
]
|
||||
)
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor Home Assistant RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Dict, List
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -110,7 +110,7 @@ from .utils import api_process, api_process_raw, api_validate, json_loads
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): str})
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
SCHEMA_OPTIONS = vol.Schema(
|
||||
@@ -118,8 +118,8 @@ SCHEMA_OPTIONS = vol.Schema(
|
||||
vol.Optional(ATTR_BOOT): vol.Coerce(AddonBoot),
|
||||
vol.Optional(ATTR_NETWORK): vol.Maybe(docker_ports),
|
||||
vol.Optional(ATTR_AUTO_UPDATE): vol.Boolean(),
|
||||
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(str),
|
||||
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(str),
|
||||
vol.Optional(ATTR_INGRESS_PANEL): vol.Boolean(),
|
||||
vol.Optional(ATTR_WATCHDOG): vol.Boolean(),
|
||||
}
|
||||
@@ -156,7 +156,7 @@ class APIAddons(CoreSysAttributes):
|
||||
return addon
|
||||
|
||||
@api_process
|
||||
async def list(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def list(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return all add-ons or repositories."""
|
||||
data_addons = [
|
||||
{
|
||||
@@ -201,7 +201,7 @@ class APIAddons(CoreSysAttributes):
|
||||
await asyncio.shield(self.sys_store.reload())
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return add-on information."""
|
||||
addon: AnyAddon = self._extract_addon(request)
|
||||
|
||||
@@ -309,7 +309,7 @@ class APIAddons(CoreSysAttributes):
|
||||
|
||||
# Extend schema with add-on specific validation
|
||||
addon_schema = SCHEMA_OPTIONS.extend(
|
||||
{vol.Optional(ATTR_OPTIONS): vol.Any(None, addon.schema)}
|
||||
{vol.Optional(ATTR_OPTIONS): vol.Maybe(addon.schema)}
|
||||
)
|
||||
|
||||
# Validate/Process Body
|
||||
@@ -390,7 +390,7 @@ class APIAddons(CoreSysAttributes):
|
||||
async def security(self, request: web.Request) -> None:
|
||||
"""Store security options for add-on."""
|
||||
addon = self._extract_addon_installed(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:
|
||||
_LOGGER.warning("Changing protected flag for %s!", addon.slug)
|
||||
@@ -399,7 +399,7 @@ class APIAddons(CoreSysAttributes):
|
||||
addon.save_persist()
|
||||
|
||||
@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."""
|
||||
addon = self._extract_addon_installed(request)
|
||||
|
||||
@@ -503,6 +503,6 @@ class APIAddons(CoreSysAttributes):
|
||||
await asyncio.shield(addon.write_stdin(data))
|
||||
|
||||
|
||||
def _pretty_services(addon: AnyAddon) -> List[str]:
|
||||
def _pretty_services(addon: AnyAddon) -> list[str]:
|
||||
"""Return a simplified services role list."""
|
||||
return [f"{name}:{access}" for name, access in addon.services_role.items()]
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor Audio RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Dict
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import attr
|
||||
@@ -56,10 +56,10 @@ SCHEMA_MUTE = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
SCHEMA_DEFAULT = vol.Schema({vol.Required(ATTR_NAME): vol.Coerce(str)})
|
||||
SCHEMA_DEFAULT = vol.Schema({vol.Required(ATTR_NAME): str})
|
||||
|
||||
SCHEMA_PROFILE = vol.Schema(
|
||||
{vol.Required(ATTR_CARD): vol.Coerce(str), vol.Required(ATTR_NAME): vol.Coerce(str)}
|
||||
{vol.Required(ATTR_CARD): str, vol.Required(ATTR_NAME): str}
|
||||
)
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class APIAudio(CoreSysAttributes):
|
||||
"""Handle RESTful API for Audio functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return Audio information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_plugins.audio.version,
|
||||
@@ -89,7 +89,7 @@ class APIAudio(CoreSysAttributes):
|
||||
}
|
||||
|
||||
@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."""
|
||||
stats = await self.sys_plugins.audio.stats()
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
"""Init file for Supervisor auth/SSO RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Dict
|
||||
|
||||
from aiohttp import BasicAuth, web
|
||||
from aiohttp.hdrs import AUTHORIZATION, CONTENT_TYPE, WWW_AUTHENTICATE
|
||||
@@ -24,12 +23,12 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_PASSWORD_RESET = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_USERNAME): vol.Coerce(str),
|
||||
vol.Required(ATTR_PASSWORD): vol.Coerce(str),
|
||||
vol.Required(ATTR_USERNAME): str,
|
||||
vol.Required(ATTR_PASSWORD): str,
|
||||
}
|
||||
)
|
||||
|
||||
REALM_HEADER: Dict[str, str] = {
|
||||
REALM_HEADER: dict[str, str] = {
|
||||
WWW_AUTHENTICATE: 'Basic realm="Home Assistant Authentication"'
|
||||
}
|
||||
|
||||
@@ -46,7 +45,7 @@ class APIAuth(CoreSysAttributes):
|
||||
return self.sys_auth.check_login(addon, auth.login, auth.password)
|
||||
|
||||
def _process_dict(
|
||||
self, request: web.Request, addon: Addon, data: Dict[str, str]
|
||||
self, request: web.Request, addon: Addon, data: dict[str, str]
|
||||
) -> bool:
|
||||
"""Process login with dict data.
|
||||
|
||||
@@ -86,7 +85,7 @@ class APIAuth(CoreSysAttributes):
|
||||
@api_process
|
||||
async def reset(self, request: web.Request) -> None:
|
||||
"""Process reset password request."""
|
||||
body: Dict[str, str] = await api_validate(SCHEMA_PASSWORD_RESET, request)
|
||||
body: dict[str, str] = await api_validate(SCHEMA_PASSWORD_RESET, request)
|
||||
await asyncio.shield(
|
||||
self.sys_auth.change_password(body[ATTR_USERNAME], body[ATTR_PASSWORD])
|
||||
)
|
||||
|
@@ -38,27 +38,25 @@ RE_SLUGIFY_NAME = re.compile(r"[^A-Za-z0-9]+")
|
||||
# pylint: disable=no-value-for-parameter
|
||||
SCHEMA_RESTORE_PARTIAL = vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
|
||||
vol.Optional(ATTR_PASSWORD): vol.Maybe(str),
|
||||
vol.Optional(ATTR_HOMEASSISTANT): vol.Boolean(),
|
||||
vol.Optional(ATTR_ADDONS): vol.All([vol.Coerce(str)], vol.Unique()),
|
||||
vol.Optional(ATTR_ADDONS): vol.All([str], vol.Unique()),
|
||||
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(ALL_FOLDERS)], vol.Unique()),
|
||||
}
|
||||
)
|
||||
|
||||
SCHEMA_RESTORE_FULL = vol.Schema(
|
||||
{vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str))}
|
||||
)
|
||||
SCHEMA_RESTORE_FULL = vol.Schema({vol.Optional(ATTR_PASSWORD): vol.Maybe(str)})
|
||||
|
||||
SCHEMA_BACKUP_FULL = vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_NAME): vol.Coerce(str),
|
||||
vol.Optional(ATTR_PASSWORD): vol.Any(None, vol.Coerce(str)),
|
||||
vol.Optional(ATTR_NAME): str,
|
||||
vol.Optional(ATTR_PASSWORD): vol.Maybe(str),
|
||||
}
|
||||
)
|
||||
|
||||
SCHEMA_BACKUP_PARTIAL = SCHEMA_BACKUP_FULL.extend(
|
||||
{
|
||||
vol.Optional(ATTR_ADDONS): vol.All([vol.Coerce(str)], vol.Unique()),
|
||||
vol.Optional(ATTR_ADDONS): vol.All([str], vol.Unique()),
|
||||
vol.Optional(ATTR_FOLDERS): vol.All([vol.In(ALL_FOLDERS)], vol.Unique()),
|
||||
vol.Optional(ATTR_HOMEASSISTANT): vol.Boolean(),
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor HA cli RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -32,7 +32,7 @@ class APICli(CoreSysAttributes):
|
||||
"""Handle RESTful API for HA Cli functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return HA cli information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_plugins.cli.version,
|
||||
@@ -41,7 +41,7 @@ class APICli(CoreSysAttributes):
|
||||
}
|
||||
|
||||
@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."""
|
||||
stats = await self.sys_plugins.cli.stats()
|
||||
|
||||
|
@@ -1,6 +1,11 @@
|
||||
"""Const for API."""
|
||||
|
||||
ATTR_USE_RTC = "use_rtc"
|
||||
ATTR_USE_NTP = "use_ntp"
|
||||
ATTR_DT_UTC = "dt_utc"
|
||||
ATTR_AGENT_VERSION = "agent_version"
|
||||
ATTR_BOOT_TIMESTAMP = "boot_timestamp"
|
||||
ATTR_DATA_DISK = "data_disk"
|
||||
ATTR_DEVICE = "device"
|
||||
ATTR_DT_SYNCHRONIZED = "dt_synchronized"
|
||||
ATTR_DT_UTC = "dt_utc"
|
||||
ATTR_STARTUP_TIME = "startup_time"
|
||||
ATTR_USE_NTP = "use_ntp"
|
||||
ATTR_USE_RTC = "use_rtc"
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor DNS RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Dict
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -40,7 +40,7 @@ class APICoreDNS(CoreSysAttributes):
|
||||
"""Handle RESTful API for DNS functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return DNS information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_plugins.dns.version,
|
||||
@@ -63,7 +63,7 @@ class APICoreDNS(CoreSysAttributes):
|
||||
self.sys_plugins.dns.save_data()
|
||||
|
||||
@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."""
|
||||
stats = await self.sys_plugins.dns.stats()
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Init file for Supervisor Home Assistant RESTful API."""
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -21,7 +21,7 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_DOCKER_REGISTRY = vol.Schema(
|
||||
{
|
||||
vol.Coerce(str): {
|
||||
str: {
|
||||
vol.Required(ATTR_USERNAME): str,
|
||||
vol.Required(ATTR_PASSWORD): str,
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class APIDocker(CoreSysAttributes):
|
||||
"""Handle RESTful API for Docker configuration."""
|
||||
|
||||
@api_process
|
||||
async def registries(self, request) -> Dict[str, Any]:
|
||||
async def registries(self, request) -> dict[str, Any]:
|
||||
"""Return the list of registries."""
|
||||
data_registries = {}
|
||||
for hostname, registry in self.sys_docker.config.registries.items():
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Init file for Supervisor hardware RESTful API."""
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
@@ -19,7 +19,7 @@ from .utils import api_process
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def device_struct(device: Device) -> Dict[str, Any]:
|
||||
def device_struct(device: Device) -> dict[str, Any]:
|
||||
"""Return a dict with information of a interface to be used in th API."""
|
||||
return {
|
||||
ATTR_NAME: device.name,
|
||||
@@ -35,7 +35,7 @@ class APIHardware(CoreSysAttributes):
|
||||
"""Handle RESTful API for hardware functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Show hardware info."""
|
||||
return {
|
||||
ATTR_DEVICES: [
|
||||
@@ -44,7 +44,7 @@ class APIHardware(CoreSysAttributes):
|
||||
}
|
||||
|
||||
@api_process
|
||||
async def audio(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def audio(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Show pulse audio profiles."""
|
||||
return {
|
||||
ATTR_AUDIO: {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor Home Assistant RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Dict
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -48,9 +48,9 @@ SCHEMA_OPTIONS = vol.Schema(
|
||||
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(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(vol.Coerce(str)),
|
||||
vol.Optional(ATTR_REFRESH_TOKEN): vol.Maybe(str),
|
||||
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(str),
|
||||
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(str),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -61,7 +61,7 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
"""Handle RESTful API for Home Assistant functions."""
|
||||
|
||||
@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 {
|
||||
ATTR_VERSION: self.sys_homeassistant.version,
|
||||
@@ -117,7 +117,7 @@ class APIHomeAssistant(CoreSysAttributes):
|
||||
self.sys_homeassistant.save_data()
|
||||
|
||||
@api_process
|
||||
async def stats(self, request: web.Request) -> Dict[Any, str]:
|
||||
async def stats(self, request: web.Request) -> dict[Any, str]:
|
||||
"""Return resource information."""
|
||||
stats = await self.sys_homeassistant.core.stats()
|
||||
if not stats:
|
||||
|
@@ -25,12 +25,20 @@ from ..const import (
|
||||
CONTENT_TYPE_BINARY,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .const import ATTR_DT_SYNCHRONIZED, ATTR_DT_UTC, ATTR_USE_NTP, ATTR_USE_RTC
|
||||
from .const import (
|
||||
ATTR_AGENT_VERSION,
|
||||
ATTR_BOOT_TIMESTAMP,
|
||||
ATTR_DT_SYNCHRONIZED,
|
||||
ATTR_DT_UTC,
|
||||
ATTR_STARTUP_TIME,
|
||||
ATTR_USE_NTP,
|
||||
ATTR_USE_RTC,
|
||||
)
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
SERVICE = "service"
|
||||
|
||||
SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_HOSTNAME): vol.Coerce(str)})
|
||||
SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_HOSTNAME): str})
|
||||
|
||||
|
||||
class APIHost(CoreSysAttributes):
|
||||
@@ -40,6 +48,7 @@ class APIHost(CoreSysAttributes):
|
||||
async def info(self, request):
|
||||
"""Return host information."""
|
||||
return {
|
||||
ATTR_AGENT_VERSION: self.sys_dbus.agent.version,
|
||||
ATTR_CHASSIS: self.sys_host.info.chassis,
|
||||
ATTR_CPE: self.sys_host.info.cpe,
|
||||
ATTR_DEPLOYMENT: self.sys_host.info.deployment,
|
||||
@@ -56,6 +65,8 @@ class APIHost(CoreSysAttributes):
|
||||
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,
|
||||
}
|
||||
|
||||
@api_process
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Init file for Supervisor info RESTful API."""
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
@@ -31,12 +31,12 @@ class APIInfo(CoreSysAttributes):
|
||||
"""Handle RESTful API for info functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Show system info."""
|
||||
return {
|
||||
ATTR_SUPERVISOR: self.sys_supervisor.version,
|
||||
ATTR_HOMEASSISTANT: self.sys_homeassistant.version,
|
||||
ATTR_HASSOS: self.sys_hassos.version,
|
||||
ATTR_HASSOS: self.sys_os.version,
|
||||
ATTR_DOCKER: self.sys_docker.info.version,
|
||||
ATTR_HOSTNAME: self.sys_host.info.hostname,
|
||||
ATTR_OPERATING_SYSTEM: self.sys_host.info.operating_system,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import asyncio
|
||||
from ipaddress import ip_address
|
||||
import logging
|
||||
from typing import Any, Dict, Union
|
||||
from typing import Any, Union
|
||||
|
||||
import aiohttp
|
||||
from aiohttp import ClientTimeout, hdrs, web
|
||||
@@ -54,7 +54,7 @@ class APIIngress(CoreSysAttributes):
|
||||
return f"http://{addon.ip_address}:{addon.ingress_port}/{path}"
|
||||
|
||||
@api_process
|
||||
async def panels(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def panels(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Create a list of panel data."""
|
||||
addons = {}
|
||||
for addon in self.sys_ingress.addons:
|
||||
@@ -69,14 +69,14 @@ class APIIngress(CoreSysAttributes):
|
||||
|
||||
@api_process
|
||||
@require_home_assistant
|
||||
async def create_session(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def create_session(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Create a new session."""
|
||||
session = self.sys_ingress.create_session()
|
||||
return {ATTR_SESSION: session}
|
||||
|
||||
@api_process
|
||||
@require_home_assistant
|
||||
async def validate_session(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def validate_session(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Validate session and extending how long it's valid for."""
|
||||
data = await api_validate(VALIDATE_SESSION_DATA, request)
|
||||
|
||||
@@ -220,7 +220,7 @@ class APIIngress(CoreSysAttributes):
|
||||
|
||||
def _init_header(
|
||||
request: web.Request, addon: str
|
||||
) -> Union[CIMultiDict, Dict[str, str]]:
|
||||
) -> Union[CIMultiDict, dict[str, str]]:
|
||||
"""Create initial header."""
|
||||
headers = {}
|
||||
|
||||
@@ -248,7 +248,7 @@ def _init_header(
|
||||
return headers
|
||||
|
||||
|
||||
def _response_header(response: aiohttp.ClientResponse) -> Dict[str, str]:
|
||||
def _response_header(response: aiohttp.ClientResponse) -> dict[str, str]:
|
||||
"""Create response header."""
|
||||
headers = {}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Init file for Supervisor Jobs RESTful API."""
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -20,7 +20,7 @@ class APIJobs(CoreSysAttributes):
|
||||
"""Handle RESTful API for OS functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return JobManager information."""
|
||||
return {
|
||||
ATTR_IGNORE_CONDITIONS: self.sys_jobs.ignore_conditions,
|
||||
|
@@ -173,7 +173,7 @@ class SecurityMiddleware(CoreSysAttributes):
|
||||
|
||||
# Observer
|
||||
if supervisor_token == self.sys_plugins.observer.supervisor_token:
|
||||
if not OBSERVER_CHECK.match(request.url):
|
||||
if not OBSERVER_CHECK.match(request.path):
|
||||
_LOGGER.warning("%s invalid Observer access", request.path)
|
||||
raise HTTPForbidden()
|
||||
_LOGGER.debug("%s access from Observer", request.path)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor Multicast RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Dict
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -34,7 +34,7 @@ class APIMulticast(CoreSysAttributes):
|
||||
"""Handle RESTful API for Multicast functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return Multicast information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_plugins.multicast.version,
|
||||
@@ -43,7 +43,7 @@ class APIMulticast(CoreSysAttributes):
|
||||
}
|
||||
|
||||
@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."""
|
||||
stats = await self.sys_plugins.multicast.stats()
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""REST API for network."""
|
||||
import asyncio
|
||||
from ipaddress import ip_address, ip_interface
|
||||
from typing import Any, Awaitable, Dict
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import attr
|
||||
@@ -82,7 +82,7 @@ SCHEMA_UPDATE = vol.Schema(
|
||||
)
|
||||
|
||||
|
||||
def ipconfig_struct(config: IpConfig) -> Dict[str, Any]:
|
||||
def ipconfig_struct(config: IpConfig) -> dict[str, Any]:
|
||||
"""Return a dict with information about ip configuration."""
|
||||
return {
|
||||
ATTR_METHOD: config.method,
|
||||
@@ -92,7 +92,7 @@ def ipconfig_struct(config: IpConfig) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def wifi_struct(config: WifiConfig) -> Dict[str, Any]:
|
||||
def wifi_struct(config: WifiConfig) -> dict[str, Any]:
|
||||
"""Return a dict with information about wifi configuration."""
|
||||
return {
|
||||
ATTR_MODE: config.mode,
|
||||
@@ -102,7 +102,7 @@ def wifi_struct(config: WifiConfig) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def vlan_struct(config: VlanConfig) -> Dict[str, Any]:
|
||||
def vlan_struct(config: VlanConfig) -> dict[str, Any]:
|
||||
"""Return a dict with information about VLAN configuration."""
|
||||
return {
|
||||
ATTR_ID: config.id,
|
||||
@@ -110,7 +110,7 @@ def vlan_struct(config: VlanConfig) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def interface_struct(interface: Interface) -> Dict[str, Any]:
|
||||
def interface_struct(interface: Interface) -> dict[str, Any]:
|
||||
"""Return a dict with information of a interface to be used in th API."""
|
||||
return {
|
||||
ATTR_INTERFACE: interface.name,
|
||||
@@ -125,7 +125,7 @@ def interface_struct(interface: Interface) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def accesspoint_struct(accesspoint: AccessPoint) -> Dict[str, Any]:
|
||||
def accesspoint_struct(accesspoint: AccessPoint) -> dict[str, Any]:
|
||||
"""Return a dict for AccessPoint."""
|
||||
return {
|
||||
ATTR_MODE: accesspoint.mode,
|
||||
@@ -158,7 +158,7 @@ class APINetwork(CoreSysAttributes):
|
||||
raise APIError(f"Interface {name} does not exist") from None
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return network information."""
|
||||
return {
|
||||
ATTR_INTERFACES: [
|
||||
@@ -176,7 +176,7 @@ class APINetwork(CoreSysAttributes):
|
||||
}
|
||||
|
||||
@api_process
|
||||
async def interface_info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def interface_info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return network information for a interface."""
|
||||
interface = self._get_interface(request.match_info.get(ATTR_INTERFACE))
|
||||
|
||||
@@ -223,7 +223,7 @@ class APINetwork(CoreSysAttributes):
|
||||
return asyncio.shield(self.sys_host.network.update())
|
||||
|
||||
@api_process
|
||||
async def scan_accesspoints(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def scan_accesspoints(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Scan and return a list of available networks."""
|
||||
interface = self._get_interface(request.match_info.get(ATTR_INTERFACE))
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
"""Init file for Supervisor Observer RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -33,7 +33,7 @@ class APIObserver(CoreSysAttributes):
|
||||
"""Handle RESTful API for Observer functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return HA Observer information."""
|
||||
return {
|
||||
ATTR_HOST: str(self.sys_docker.network.observer),
|
||||
@@ -43,7 +43,7 @@ class APIObserver(CoreSysAttributes):
|
||||
}
|
||||
|
||||
@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."""
|
||||
stats = await self.sys_plugins.observer.stats()
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
"""Init file for Supervisor HassOS RESTful API."""
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Dict
|
||||
from pathlib import Path
|
||||
from typing import Any, Awaitable
|
||||
|
||||
from aiohttp import web
|
||||
import voluptuous as vol
|
||||
@@ -9,42 +10,60 @@ import voluptuous as vol
|
||||
from ..const import (
|
||||
ATTR_BOARD,
|
||||
ATTR_BOOT,
|
||||
ATTR_DEVICES,
|
||||
ATTR_UPDATE_AVAILABLE,
|
||||
ATTR_VERSION,
|
||||
ATTR_VERSION_LATEST,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..validate import version_tag
|
||||
from .const import ATTR_DATA_DISK, ATTR_DEVICE
|
||||
from .utils import api_process, api_validate
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): version_tag})
|
||||
SCHEMA_DISK = vol.Schema({vol.Required(ATTR_DEVICE): vol.All(str, vol.Coerce(Path))})
|
||||
|
||||
|
||||
class APIOS(CoreSysAttributes):
|
||||
"""Handle RESTful API for OS functions."""
|
||||
|
||||
@api_process
|
||||
async def info(self, request: web.Request) -> Dict[str, Any]:
|
||||
async def info(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return OS information."""
|
||||
return {
|
||||
ATTR_VERSION: self.sys_hassos.version,
|
||||
ATTR_VERSION_LATEST: self.sys_hassos.latest_version,
|
||||
ATTR_UPDATE_AVAILABLE: self.sys_hassos.need_update,
|
||||
ATTR_BOARD: self.sys_hassos.board,
|
||||
ATTR_VERSION: self.sys_os.version,
|
||||
ATTR_VERSION_LATEST: self.sys_os.latest_version,
|
||||
ATTR_UPDATE_AVAILABLE: self.sys_os.need_update,
|
||||
ATTR_BOARD: self.sys_os.board,
|
||||
ATTR_BOOT: self.sys_dbus.rauc.boot_slot,
|
||||
ATTR_DATA_DISK: self.sys_os.datadisk.disk_used,
|
||||
}
|
||||
|
||||
@api_process
|
||||
async def update(self, request: web.Request) -> None:
|
||||
"""Update OS."""
|
||||
body = await api_validate(SCHEMA_VERSION, request)
|
||||
version = body.get(ATTR_VERSION, self.sys_hassos.latest_version)
|
||||
version = body.get(ATTR_VERSION, self.sys_os.latest_version)
|
||||
|
||||
await asyncio.shield(self.sys_hassos.update(version))
|
||||
await asyncio.shield(self.sys_os.update(version))
|
||||
|
||||
@api_process
|
||||
def config_sync(self, request: web.Request) -> Awaitable[None]:
|
||||
"""Trigger config reload on OS."""
|
||||
return asyncio.shield(self.sys_hassos.config_sync())
|
||||
return asyncio.shield(self.sys_os.config_sync())
|
||||
|
||||
@api_process
|
||||
async def migrate_data(self, request: web.Request) -> None:
|
||||
"""Trigger data disk migration on Host."""
|
||||
body = await api_validate(SCHEMA_DISK, request)
|
||||
|
||||
await asyncio.shield(self.sys_os.datadisk.migrate_disk(body[ATTR_DEVICE]))
|
||||
|
||||
@api_process
|
||||
async def list_data(self, request: web.Request) -> dict[str, Any]:
|
||||
"""Return possible data targets."""
|
||||
return {
|
||||
ATTR_DEVICES: self.sys_os.datadisk.available_disks,
|
||||
}
|
||||
|
@@ -1,14 +1,14 @@
|
||||
|
||||
function loadES5() {
|
||||
var el = document.createElement('script');
|
||||
el.src = '/api/hassio/app/frontend_es5/entrypoint.e3a9299f.js';
|
||||
el.src = '/api/hassio/app/frontend_es5/entrypoint.4bb3aeeb.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.48b24b78.js')")();
|
||||
new Function("import('/api/hassio/app/frontend_latest/entrypoint.9dbee840.js')")();
|
||||
} catch (err) {
|
||||
loadES5();
|
||||
}
|
||||
|
Binary file not shown.
@@ -1,2 +0,0 @@
|
||||
(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([[534],{68441:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=new WeakMap;t.default=function(e){var t=l.get(e);return t||(t=Object.create(null),l.set(e,t)),t}},78643:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PluralRules=void 0;var a=l(87480),n=l(43965),r=a.__importDefault(l(68441));function o(e,t){if(!(e instanceof u))throw new TypeError("Method Intl.PluralRules.prototype."+t+" called on incompatible receiver "+String(e))}function i(e,t,l,a){var n=a.IntegerDigits,r=a.NumberOfFractionDigits,o=a.FractionDigits;return u.localeData[e].fn(r?n+"."+o:n,"ordinal"===t)}var u=function(){function e(t,l){if(!(this&&this instanceof e?this.constructor:void 0))throw new TypeError("Intl.PluralRules must be called with 'new'");return n.InitializePluralRules(this,t,l,{availableLocales:e.availableLocales,relevantExtensionKeys:e.relevantExtensionKeys,localeData:e.localeData,getDefaultLocale:e.getDefaultLocale,getInternalSlots:r.default})}return e.prototype.resolvedOptions=function(){o(this,"resolvedOptions");var t=Object.create(null),l=r.default(this);return t.locale=l.locale,t.type=l.type,["minimumIntegerDigits","minimumFractionDigits","maximumFractionDigits","minimumSignificantDigits","maximumSignificantDigits"].forEach((function(e){var a=l[e];void 0!==a&&(t[e]=a)})),t.pluralCategories=a.__spreadArray([],e.localeData[t.locale].categories[t.type]),t},e.prototype.select=function(e){o(this,"select");var t=n.ToNumber(e);return n.ResolvePlural(this,t,{getInternalSlots:r.default,PluralRuleSelect:i})},e.prototype.toString=function(){return"[object Intl.PluralRules]"},e.supportedLocalesOf=function(t,l){return n.SupportedLocales(e.availableLocales,n.CanonicalizeLocaleList(t),l)},e.__addLocaleData=function(){for(var t=[],l=0;l<arguments.length;l++)t[l]=arguments[l];for(var a=0,n=t;a<n.length;a++){var r=n[a],o=r.data,i=r.locale;e.localeData[i]=o,e.availableLocales.add(i),e.__defaultLocale||(e.__defaultLocale=i)}},e.getDefaultLocale=function(){return e.__defaultLocale},e.localeData={},e.availableLocales=new Set,e.__defaultLocale="",e.relevantExtensionKeys=[],e.polyfilled=!0,e}();t.PluralRules=u;try{"undefined"!=typeof Symbol&&Object.defineProperty(u.prototype,Symbol.toStringTag,{value:"Intl.PluralRules",writable:!1,enumerable:!1,configurable:!0});try{Object.defineProperty(u,"length",{value:0,writable:!1,enumerable:!1,configurable:!0})}catch(c){}Object.defineProperty(u.prototype.constructor,"length",{value:0,writable:!1,enumerable:!1,configurable:!0}),Object.defineProperty(u.supportedLocalesOf,"length",{value:1,writable:!1,enumerable:!1,configurable:!0})}catch(s){}},25534:function(e,t,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=l(78643);l(27815).shouldPolyfill()&&Object.defineProperty(Intl,"PluralRules",{value:a.PluralRules,writable:!0,enumerable:!1,configurable:!0})},27815:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.shouldPolyfill=void 0,t.shouldPolyfill=function(){return"undefined"==typeof Intl||!("PluralRules"in Intl)||"one"===new Intl.PluralRules("en",{minimumFractionDigits:2}).select(1)}}}]);
|
||||
//# sourceMappingURL=01378878.js.map
|
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"01378878.js","sources":["webpack://home-assistant-frontend/01378878.js"],"mappings":"AAAA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"02e63dc5.js","sources":["webpack://home-assistant-frontend/02e63dc5.js"],"mappings":"AAAA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/069b642c.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/069b642c.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/0989d194.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/0989d194.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"0bbd5196.js","sources":["webpack://home-assistant-frontend/0bbd5196.js"],"mappings":";AAAA","sourceRoot":""}
|
1
supervisor/api/panel/frontend_es5/28fc574d.js
Normal file
1
supervisor/api/panel/frontend_es5/28fc574d.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/28fc574d.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/28fc574d.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"2995ba79.js","sources":["webpack://home-assistant-frontend/2995ba79.js"],"mappings":"AAAA","sourceRoot":""}
|
1
supervisor/api/panel/frontend_es5/2dbdaab4.js
Normal file
1
supervisor/api/panel/frontend_es5/2dbdaab4.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/2dbdaab4.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/2dbdaab4.js.gz
Normal file
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"2f1a948c.js","sources":["webpack://home-assistant-frontend/2f1a948c.js"],"mappings":"AAAA","sourceRoot":""}
|
1
supervisor/api/panel/frontend_es5/37f808e6.js
Normal file
1
supervisor/api/panel/frontend_es5/37f808e6.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/37f808e6.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/37f808e6.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_es5/432539dc.js
Normal file
1
supervisor/api/panel/frontend_es5/432539dc.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/432539dc.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/432539dc.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_es5/44b59b80.js
Normal file
1
supervisor/api/panel/frontend_es5/44b59b80.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/44b59b80.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/44b59b80.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_es5/4a274bef.js
Normal file
1
supervisor/api/panel/frontend_es5/4a274bef.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/4a274bef.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/4a274bef.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"4b7385c7.js","sources":["webpack://home-assistant-frontend/4b7385c7.js"],"mappings":"AAAA","sourceRoot":""}
|
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"5891fe3e.js","sources":["webpack://home-assistant-frontend/5891fe3e.js"],"mappings":";AAAA","sourceRoot":""}
|
1
supervisor/api/panel/frontend_es5/64fa61b0.js
Normal file
1
supervisor/api/panel/frontend_es5/64fa61b0.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/64fa61b0.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/64fa61b0.js.gz
Normal file
Binary file not shown.
2
supervisor/api/panel/frontend_es5/69e58998.js
Normal file
2
supervisor/api/panel/frontend_es5/69e58998.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/69e58998.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/69e58998.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_es5/69f9cc55.js
Normal file
1
supervisor/api/panel/frontend_es5/69f9cc55.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/69f9cc55.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/69f9cc55.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"6aa86e64.js","sources":["webpack://home-assistant-frontend/6aa86e64.js"],"mappings":"AAAA","sourceRoot":""}
|
@@ -1,2 +1 @@
|
||||
!function(){"use strict";var r,t,n={5425:function(r,t,n){var e=n(91107);n(58556);function o(r,t){return function(r){if(Array.isArray(r))return r}(r)||function(r,t){var n=null==r?null:"undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(null==n)return;var e,o,u=[],i=!0,a=!1;try{for(n=n.call(r);!(i=(e=n.next()).done)&&(u.push(e.value),!t||u.length!==t);i=!0);}catch(f){a=!0,o=f}finally{try{i||null==n.return||n.return()}finally{if(a)throw o}}return u}(r,t)||function(r,t){if(!r)return;if("string"==typeof r)return u(r,t);var n=Object.prototype.toString.call(r).slice(8,-1);"Object"===n&&r.constructor&&(n=r.constructor.name);if("Map"===n||"Set"===n)return Array.from(r);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return u(r,t)}(r,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(r,t){(null==t||t>r.length)&&(t=r.length);for(var n=0,e=new Array(t);n<t;n++)e[n]=r[n];return e}var i={filterData:function(r,t,n){return n=n.toUpperCase(),r.filter((function(r){return Object.entries(t).some((function(t){var e=o(t,2),u=e[0],i=e[1];return!(!i.filterable||!String(i.filterKey?r[i.valueColumn||u][i.filterKey]:r[i.valueColumn||u]).toUpperCase().includes(n))}))}))},sortData:function(r,t,n,e){return r.sort((function(r,o){var u=1;"desc"===n&&(u=-1);var i=t.filterKey?r[t.valueColumn||e][t.filterKey]:r[t.valueColumn||e],a=t.filterKey?o[t.valueColumn||e][t.filterKey]:o[t.valueColumn||e];return"string"==typeof i&&(i=i.toUpperCase()),"string"==typeof a&&(a=a.toUpperCase()),void 0===i&&void 0!==a?1:void 0===a&&void 0!==i?-1:i<a?-1*u:i>a?1*u:0}))}};(0,e.Jj)(i)}},e={};function o(r){var t=e[r];if(void 0!==t)return t.exports;var u=e[r]={exports:{}};return n[r](u,u.exports,o),u.exports}o.m=n,o.x=function(){var r=o.O(void 0,[354],(function(){return o(5425)}));return r=o.O(r)},r=[],o.O=function(t,n,e,u){if(!n){var i=1/0;for(l=0;l<r.length;l++){n=r[l][0],e=r[l][1],u=r[l][2];for(var a=!0,f=0;f<n.length;f++)(!1&u||i>=u)&&Object.keys(o.O).every((function(r){return o.O[r](n[f])}))?n.splice(f--,1):(a=!1,u<i&&(i=u));a&&(r.splice(l--,1),t=e())}return t}u=u||0;for(var l=r.length;l>0&&r[l-1][2]>u;l--)r[l]=r[l-1];r[l]=[n,e,u]},o.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return o.d(t,{a:t}),t},o.d=function(r,t){for(var n in t)o.o(t,n)&&!o.o(r,n)&&Object.defineProperty(r,n,{enumerable:!0,get:t[n]})},o.f={},o.e=function(r){return Promise.all(Object.keys(o.f).reduce((function(t,n){return o.f[n](r,t),t}),[]))},o.u=function(r){return"e612d98f.js"},o.o=function(r,t){return Object.prototype.hasOwnProperty.call(r,t)},o.p="/api/hassio/app/frontend_es5/",function(){var r={425:1,477:1};o.f.i=function(t,n){r[t]||importScripts(o.p+o.u(t))};var t=self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[],n=t.push.bind(t);t.push=function(t){var e=t[0],u=t[1],i=t[2];for(var a in u)o.o(u,a)&&(o.m[a]=u[a]);for(i&&i(o);e.length;)r[e.pop()]=1;n(t)}}(),t=o.x,o.x=function(){return o.e(354).then(t)};o.x()}();
|
||||
//# sourceMappingURL=799b0b1b.js.map
|
||||
!function(){"use strict";var r,t,n={5425:function(r,t,n){var e=n(93217);n(58556);function o(r,t){return function(r){if(Array.isArray(r))return r}(r)||function(r,t){var n=null==r?null:"undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(null==n)return;var e,o,u=[],i=!0,a=!1;try{for(n=n.call(r);!(i=(e=n.next()).done)&&(u.push(e.value),!t||u.length!==t);i=!0);}catch(f){a=!0,o=f}finally{try{i||null==n.return||n.return()}finally{if(a)throw o}}return u}(r,t)||function(r,t){if(!r)return;if("string"==typeof r)return u(r,t);var n=Object.prototype.toString.call(r).slice(8,-1);"Object"===n&&r.constructor&&(n=r.constructor.name);if("Map"===n||"Set"===n)return Array.from(r);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return u(r,t)}(r,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(r,t){(null==t||t>r.length)&&(t=r.length);for(var n=0,e=new Array(t);n<t;n++)e[n]=r[n];return e}var i={filterData:function(r,t,n){return n=n.toUpperCase(),r.filter((function(r){return Object.entries(t).some((function(t){var e=o(t,2),u=e[0],i=e[1];return!(!i.filterable||!String(i.filterKey?r[i.valueColumn||u][i.filterKey]:r[i.valueColumn||u]).toUpperCase().includes(n))}))}))},sortData:function(r,t,n,e){return r.sort((function(r,o){var u=1;"desc"===n&&(u=-1);var i=t.filterKey?r[t.valueColumn||e][t.filterKey]:r[t.valueColumn||e],a=t.filterKey?o[t.valueColumn||e][t.filterKey]:o[t.valueColumn||e];return"string"==typeof i&&(i=i.toUpperCase()),"string"==typeof a&&(a=a.toUpperCase()),void 0===i&&void 0!==a?1:void 0===a&&void 0!==i?-1:i<a?-1*u:i>a?1*u:0}))}};(0,e.Jj)(i)}},e={};function o(r){var t=e[r];if(void 0!==t)return t.exports;var u=e[r]={exports:{}};return n[r](u,u.exports,o),u.exports}o.m=n,o.x=function(){var r=o.O(void 0,[191],(function(){return o(5425)}));return r=o.O(r)},r=[],o.O=function(t,n,e,u){if(!n){var i=1/0;for(c=0;c<r.length;c++){n=r[c][0],e=r[c][1],u=r[c][2];for(var a=!0,f=0;f<n.length;f++)(!1&u||i>=u)&&Object.keys(o.O).every((function(r){return o.O[r](n[f])}))?n.splice(f--,1):(a=!1,u<i&&(i=u));if(a){r.splice(c--,1);var l=e();void 0!==l&&(t=l)}}return t}u=u||0;for(var c=r.length;c>0&&r[c-1][2]>u;c--)r[c]=r[c-1];r[c]=[n,e,u]},o.n=function(r){var t=r&&r.__esModule?function(){return r.default}:function(){return r};return o.d(t,{a:t}),t},o.d=function(r,t){for(var n in t)o.o(t,n)&&!o.o(r,n)&&Object.defineProperty(r,n,{enumerable:!0,get:t[n]})},o.f={},o.e=function(r){return Promise.all(Object.keys(o.f).reduce((function(t,n){return o.f[n](r,t),t}),[]))},o.u=function(r){return"2dbdaab4.js"},o.o=function(r,t){return Object.prototype.hasOwnProperty.call(r,t)},o.p="/api/hassio/app/frontend_es5/",function(){var r={477:1,425:1};o.f.i=function(t,n){r[t]||importScripts(o.p+o.u(t))};var t=self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[],n=t.push.bind(t);t.push=function(t){var e=t[0],u=t[1],i=t[2];for(var a in u)o.o(u,a)&&(o.m[a]=u[a]);for(i&&i(o);e.length;)r[e.pop()]=1;n(t)}}(),t=o.x,o.x=function(){return o.e(191).then(t)};o.x()}();
|
BIN
supervisor/api/panel/frontend_es5/6b0926eb.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/6b0926eb.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_es5/6eb51463.js
Normal file
1
supervisor/api/panel/frontend_es5/6eb51463.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/6eb51463.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/6eb51463.js.gz
Normal file
Binary file not shown.
1
supervisor/api/panel/frontend_es5/72c04451.js
Normal file
1
supervisor/api/panel/frontend_es5/72c04451.js
Normal file
File diff suppressed because one or more lines are too long
BIN
supervisor/api/panel/frontend_es5/72c04451.js.gz
Normal file
BIN
supervisor/api/panel/frontend_es5/72c04451.js.gz
Normal file
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"799b0b1b.js","sources":["webpack://home-assistant-frontend/799b0b1b.js"],"mappings":"AAAA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"7f7c3271.js","sources":["webpack://home-assistant-frontend/7f7c3271.js"],"mappings":"AAAA","sourceRoot":""}
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user