Compare commits

...

40 Commits

Author SHA1 Message Date
Mike Degatano
df030e6209 No bool conversion in resolution test (#3826) 2022-08-26 16:42:45 +02:00
Mike Degatano
09d60b4957 Store load ignores supervisor updated condition (#3823) 2022-08-26 10:22:55 +02:00
dependabot[bot]
004065ae33 Bump pyudev from 0.23.2 to 0.24.0 (#3824)
Bumps [pyudev](https://github.com/pyudev/pyudev) from 0.23.2 to 0.24.0.
- [Release notes](https://github.com/pyudev/pyudev/releases)
- [Changelog](https://github.com/pyudev/pyudev/blob/master/CHANGES.rst)
- [Commits](https://github.com/pyudev/pyudev/compare/v0.23.2...v0.24.0)

---
updated-dependencies:
- dependency-name: pyudev
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-26 09:56:10 +02:00
Mike Degatano
854d337dd3 Bump ruamel.yaml to 0.17.21 (#3821) 2022-08-25 16:33:07 -04:00
Mike Degatano
2c5bb3f714 Fix suggestion test to be order agnostic (#3822) 2022-08-25 16:06:47 -04:00
Pascal Vizeli
7b63544474 Simplify watchdog container rules (#3820) 2022-08-25 15:20:14 -04:00
Joakim Sørensen
97af1fc66e Bump frontend to 255cb23c (#3819) 2022-08-25 10:10:16 -04:00
Mike Degatano
32d65722e9 Handle multiple suggestions with different references (#3817) 2022-08-25 10:09:25 -04:00
Mike Degatano
d5f9fcfdc7 Fire events on issue changes (#3818)
* Fire events on issue changes

* API includes if suggestion has autofix
2022-08-25 11:42:31 +02:00
dependabot[bot]
ffa524d3a4 Bump actions/cache from 3.0.7 to 3.0.8 (#3816)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.7 to 3.0.8.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.7...v3.0.8)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-23 10:53:25 -04:00
Stefan Agner
9c7de4a6c3 Improve system behavior on low memory situation (#3781)
* Improve system behavior on low memory situation

Adjust OOM killer score to prevent crucial services from getting
selected.

* Adjust OOM score of DNS plug-in as well
2022-08-23 16:02:51 +02:00
Mike Degatano
b4e1e3e853 Identify and handle dhcp issues (#3806)
* Identify and handle dhcp issues

* Change test from DHCP to Connection Problem
2022-08-23 13:57:16 +02:00
Mike Degatano
c7f7fbd41a Remove static-only fields when method is auto (#3810) 2022-08-23 13:56:14 +02:00
Mike Degatano
cbddca2658 Rename dns checks to fit pattern (#3811)
* Rename dns checks to fit pattern

* Missed a patch reference
2022-08-23 13:54:22 +02:00
Mike Degatano
f4811a0243 Watchdog addon on successful but unexpected exit (#3815) 2022-08-22 20:29:27 -04:00
dependabot[bot]
024b813865 Bump awesomeversion from 22.6.0 to 22.8.0 (#3808)
Bumps [awesomeversion](https://github.com/ludeeus/awesomeversion) from 22.6.0 to 22.8.0.
- [Release notes](https://github.com/ludeeus/awesomeversion/releases)
- [Commits](https://github.com/ludeeus/awesomeversion/compare/22.6.0...22.8.0)

---
updated-dependencies:
- dependency-name: awesomeversion
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 16:31:41 -04:00
Stefan Agner
5919bc2252 Create asyncio loop explicitly (#3804)
Creating the asyncio loop explicitly. This fixes the following
deprecation warning on Python 3.10:
/usr/src/supervisor/supervisor/__main__.py:31: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()
2022-08-19 10:01:01 -04:00
Stefan Agner
8bca34ec6b Update architecture compatibility JSON file (#3801)
* Update architecture compatibility JSON file

In particular, this enables 32-bit Arm add-ons for Yellow.

* Set correct hassio machine for odroid-xu4 (which is odroid-xu)
2022-08-19 10:11:01 +02:00
Mike Degatano
8b5e96a8ad Set autoreconnect to true for interfaces (#3807) 2022-08-19 10:09:44 +02:00
dependabot[bot]
2d908ffcec Bump docker from 5.0.3 to 6.0.0 (#3809)
Bumps [docker](https://github.com/docker/docker-py) from 5.0.3 to 6.0.0.
- [Release notes](https://github.com/docker/docker-py/releases)
- [Commits](https://github.com/docker/docker-py/compare/5.0.3...6.0.0)

---
updated-dependencies:
- dependency-name: docker
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-19 10:08:30 +02:00
Mike Degatano
c3f7a45d61 Fix memory calculation for cgroupv2 (#3802) 2022-08-17 16:50:08 -04:00
dependabot[bot]
97b05c2078 Bump time-machine from 2.8.0 to 2.8.1 (#3799)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 09:48:00 +02:00
dependabot[bot]
aa9a774939 Bump home-assistant/builder from 2022.06.2 to 2022.07.0 (#3713)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 09:30:03 +02:00
dependabot[bot]
3388a13693 Bump release-drafter/release-drafter from 5.20.0 to 5.20.1 (#3797)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 09:10:58 +02:00
dependabot[bot]
9957e3dd4c Bump coverage from 6.4.3 to 6.4.4 (#3798)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 09:03:45 +02:00
dependabot[bot]
01c2bd1b0c Bump sentry-sdk from 1.9.4 to 1.9.5 (#3800)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-17 08:58:41 +02:00
Mike Degatano
2cd7f9d1b0 Attempt plugin update before failing job condition (#3796) 2022-08-17 07:36:05 +02:00
Mike Degatano
5fc9484f73 Supervisor updated before addon repositories (#3795) 2022-08-17 07:28:06 +02:00
Pascal Vizeli
e6dfe83d62 Handle timedrift better if not synchronized (#3783)
* Handle timedrift better if not synchronized

* fix description

* adjust days

* Implement feedback

* Whoami error

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2022-08-16 16:04:17 -04:00
Franck Nijhof
3f88236495 Support for Docker manifests base images add-on build (#3724)
* Support for Docker manifests base images add-on build

* Set platform for build and tests

* Remove empty test

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2022-08-16 14:34:32 +02:00
Mike Degatano
96065ed704 Bump to python 3.10 and alpine 3.16 (#3791)
* Bump to python 3.10

* 3.10 is not a number

* Musllinux wheels link

* Revert attrs 22.1.0 -> 21.2.0 for wheel

* Revert cryptography for wheel & pylint fix

* Precommit and devcontainer to 3.10

* pyupgrade rewriting things

* revert

* Update builder.yml

* fix rust

* Update builder.yml

Co-authored-by: Pascal Vizeli <pvizeli@syshack.ch>
2022-08-16 14:33:23 +02:00
dependabot[bot]
7754424cb8 Bump time-machine from 2.7.1 to 2.8.0 (#3793)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-16 11:37:48 +02:00
dependabot[bot]
be842d5e6c Bump debugpy from 1.6.2 to 1.6.3 (#3794)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-16 11:35:05 +02:00
Mike Degatano
c8f184f24c Add auto update option (#3769)
* Add update freeze option

* Freeze to auto update and plugin condition

* Add tests

* Add supervisor_version evaluation

* OS updates require supervisor up to date

* Run version check during startup
2022-08-15 12:13:22 -04:00
dependabot[bot]
e82cb5da45 Bump sentry-sdk from 1.9.3 to 1.9.4 (#3789)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.9.3 to 1.9.4.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.9.3...1.9.4)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-12 11:33:37 -04:00
dependabot[bot]
a968f6e90a Bump actions/cache from 3.0.6 to 3.0.7 (#3788)
Bumps [actions/cache](https://github.com/actions/cache) from 3.0.6 to 3.0.7.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3.0.6...v3.0.7)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-12 11:33:00 -04:00
Mike Degatano
3eac3a6178 Absolute imports to relative imports (#3787) 2022-08-12 10:42:40 +02:00
dependabot[bot]
b831dce443 Bump sentry-sdk from 1.9.2 to 1.9.3 (#3782)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-10 11:40:41 +02:00
Mike Degatano
e62324e43f Set limits on watchdog retries (#3779)
* Set limits on watchdog retries

* Use relative import
2022-08-09 11:44:35 -04:00
Stefan Agner
a92058e6fc Mark CGroup V2 supported on Home Assistant OS (#3778)
* Mark CGroup V2 supported on Home Assistant OS

* Fix tests

* Fix comparision

* Move evaluation of CGroup after OSManager has been initialized
2022-08-09 11:32:51 +02:00
282 changed files with 3832 additions and 2057 deletions

View File

@@ -10,7 +10,7 @@
"visualstudioexptteam.vscodeintellicode",
"esbenp.prettier-vscode"
],
"mounts": [ "type=volume,target=/var/lib/docker" ],
"mounts": ["type=volume,target=/var/lib/docker"],
"settings": {
"terminal.integrated.profiles.linux": {
"zsh": {
@@ -26,7 +26,7 @@
"python.linting.pylintEnabled": true,
"python.linting.enabled": true,
"python.formatting.provider": "black",
"python.formatting.blackArgs": ["--target-version", "py39"],
"python.formatting.blackArgs": ["--target-version", "py310"],
"python.formatting.blackPath": "/usr/local/bin/black",
"python.linting.banditPath": "/usr/local/bin/bandit",
"python.linting.flake8Path": "/usr/local/bin/flake8",

View File

@@ -33,10 +33,9 @@ on:
- setup.py
env:
DEFAULT_PYTHON: 3.9
DEFAULT_PYTHON: "3.10"
BUILD_NAME: supervisor
BUILD_TYPE: supervisor
WHEELS_TAG: 3.9-alpine3.14
jobs:
init:
@@ -88,18 +87,26 @@ jobs:
uses: actions/checkout@v3.0.2
with:
fetch-depth: 0
- name: Write env-file
if: needs.init.outputs.requirements == 'true'
run: |
(
# Fix out of memory issues with rust
echo "CARGO_NET_GIT_FETCH_WITH_CLI=true"
) > .env_file
- name: Build wheels
if: needs.init.outputs.requirements == 'true'
uses: home-assistant/wheels@2022.01.2
uses: home-assistant/wheels@2022.06.7
with:
tag: ${{ env.WHEELS_TAG }}
abi: cp310
tag: musllinux_1_2
arch: ${{ matrix.arch }}
wheels-host: wheels.hass.io
wheels-key: ${{ secrets.WHEELS_KEY }}
wheels-user: wheels
apk: "build-base;libffi-dev;openssl-dev;cargo"
apk: "libffi-dev;openssl-dev"
skip-binary: aiohttp
env-file: true
requirements: "requirements.txt"
- name: Set version
@@ -128,7 +135,7 @@ jobs:
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
- name: Build supervisor
uses: home-assistant/builder@2022.06.2
uses: home-assistant/builder@2022.07.0
with:
args: |
$BUILD_ARGS \
@@ -213,7 +220,7 @@ jobs:
- name: Build the Supervisor
if: needs.init.outputs.publish != 'true'
uses: home-assistant/builder@2022.06.2
uses: home-assistant/builder@2022.07.0
with:
args: |
--test \

View File

@@ -8,7 +8,7 @@ on:
pull_request: ~
env:
DEFAULT_PYTHON: 3.9
DEFAULT_PYTHON: "3.10"
PRE_COMMIT_HOME: ~/.cache/pre-commit
DEFAULT_CAS: v1.0.2
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]
python-version: ["3.10"]
name: Prepare Python ${{ matrix.python-version }} dependencies
steps:
- name: Check out code from GitHub
@@ -31,7 +31,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -45,7 +45,7 @@ jobs:
pip install -r requirements.txt -r requirements_tests.txt
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -72,7 +72,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -116,7 +116,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -128,7 +128,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -160,7 +160,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -192,7 +192,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -204,7 +204,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -233,7 +233,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -245,7 +245,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -277,7 +277,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -309,7 +309,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -321,7 +321,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: ${{ env.PRE_COMMIT_HOME }}
key: |
@@ -341,7 +341,7 @@ jobs:
needs: prepare
strategy:
matrix:
python-version: [3.9]
python-version: ["3.10"]
name: Run tests Python ${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
@@ -357,7 +357,7 @@ jobs:
version: ${{ env.DEFAULT_CAS }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |
@@ -411,7 +411,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v3.0.6
uses: actions/cache@v3.0.8
with:
path: venv
key: |

View File

@@ -36,7 +36,7 @@ jobs:
echo "::set-output name=version::$datepre.$newpost"
- name: Run Release Drafter
uses: release-drafter/release-drafter@v5.20.0
uses: release-drafter/release-drafter@v5.20.1
with:
tag: ${{ steps.version.outputs.version }}
name: ${{ steps.version.outputs.version }}

View File

@@ -7,7 +7,7 @@ repos:
- --safe
- --quiet
- --target-version
- py39
- py310
files: ^((supervisor|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
@@ -31,4 +31,4 @@ repos:
rev: v2.32.1
hooks:
- id: pyupgrade
args: [--py39-plus]
args: [--py310-plus]

View File

@@ -6,7 +6,6 @@ ENV \
SUPERVISOR_API=http://localhost
ARG \
BUILD_ARCH \
CAS_VERSION
# Install base
@@ -40,7 +39,7 @@ COPY requirements.txt .
RUN \
export MAKEFLAGS="-j$(nproc)" \
&& pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links \
"https://wheels.home-assistant.io/alpine-$(cut -d '.' -f 1-2 < /etc/alpine-release)/${BUILD_ARCH}/" \
"https://wheels.home-assistant.io/musllinux/" \
-r ./requirements.txt \
&& rm -f requirements.txt

View File

@@ -1,11 +1,11 @@
image: homeassistant/{arch}-hassio-supervisor
shadow_repository: ghcr.io/home-assistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.9-alpine3.14
armhf: ghcr.io/home-assistant/armhf-base-python:3.9-alpine3.14
armv7: ghcr.io/home-assistant/armv7-base-python:3.9-alpine3.14
amd64: ghcr.io/home-assistant/amd64-base-python:3.9-alpine3.14
i386: ghcr.io/home-assistant/i386-base-python:3.9-alpine3.14
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.10-alpine3.16
armhf: ghcr.io/home-assistant/armhf-base-python:3.10-alpine3.16
armv7: ghcr.io/home-assistant/armv7-base-python:3.10-alpine3.16
amd64: ghcr.io/home-assistant/amd64-base-python:3.10-alpine3.16
i386: ghcr.io/home-assistant/i386-base-python:3.10-alpine3.16
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io

View File

@@ -3,23 +3,23 @@ aiohttp==3.8.1
async_timeout==4.0.2
atomicwrites-homeassistant==1.4.1
attrs==22.1.0
awesomeversion==22.6.0
awesomeversion==22.8.0
brotli==1.0.9
cchardet==2.1.7
ciso8601==2.2.0
colorlog==6.6.0
cpe==1.2.1
cryptography==37.0.4
debugpy==1.6.2
debugpy==1.6.3
deepmerge==1.0.1
dirhash==0.2.1
docker==5.0.3
docker==6.0.0
gitpython==3.1.27
jinja2==3.1.2
pulsectl==22.3.2
pyudev==0.23.2
ruamel.yaml==0.17.17
pyudev==0.24.0
ruamel.yaml==0.17.21
securetar==2022.2.0
sentry-sdk==1.9.2
sentry-sdk==1.9.5
voluptuous==0.13.1
dbus-next==0.2.3

View File

@@ -1,6 +1,6 @@
black==22.6.0
codecov==2.1.12
coverage==6.4.3
coverage==6.4.4
flake8-docstrings==1.6.0
flake8==5.0.4
pre-commit==2.20.0
@@ -12,4 +12,4 @@ pytest-cov==3.0.0
pytest-timeout==2.1.0
pytest==7.1.2
pyupgrade==2.37.3
time-machine==2.7.1
time-machine==2.8.1

View File

@@ -28,7 +28,8 @@ if __name__ == "__main__":
bootstrap.initialize_logging()
# Init async event loop
loop = asyncio.get_event_loop()
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Check if all information are available to setup Supervisor
bootstrap.check_environment()

View File

@@ -3,7 +3,7 @@ import asyncio
from contextlib import suppress
import logging
import tarfile
from typing import Optional, Union
from typing import Union
from ..const import AddonBoot, AddonStartup, AddonState
from ..coresys import CoreSys, CoreSysAttributes
@@ -24,6 +24,7 @@ from ..resolution.const import ContextType, IssueType, SuggestionType
from ..store.addon import AddonStore
from ..utils import check_exception_chain
from .addon import Addon
from .const import ADDON_UPDATE_CONDITIONS
from .data import AddonsData
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -52,7 +53,7 @@ class AddonManager(CoreSysAttributes):
"""Return a list of all installed add-ons."""
return list(self.local.values())
def get(self, addon_slug: str, local_only: bool = False) -> Optional[AnyAddon]:
def get(self, addon_slug: str, local_only: bool = False) -> AnyAddon | None:
"""Return an add-on from slug.
Prio:
@@ -65,7 +66,7 @@ class AddonManager(CoreSysAttributes):
return self.store.get(addon_slug)
return None
def from_token(self, token: str) -> Optional[Addon]:
def from_token(self, token: str) -> Addon | None:
"""Return an add-on from Supervisor token."""
for addon in self.installed:
if token == addon.supervisor_token:
@@ -144,11 +145,7 @@ class AddonManager(CoreSysAttributes):
self.sys_capture_exception(err)
@Job(
conditions=[
JobCondition.FREE_SPACE,
JobCondition.INTERNET_HOST,
JobCondition.HEALTHY,
],
conditions=ADDON_UPDATE_CONDITIONS,
on_condition=AddonsJobError,
)
async def install(self, slug: str) -> None:
@@ -246,14 +243,10 @@ class AddonManager(CoreSysAttributes):
_LOGGER.info("Add-on '%s' successfully removed", slug)
@Job(
conditions=[
JobCondition.FREE_SPACE,
JobCondition.INTERNET_HOST,
JobCondition.HEALTHY,
],
conditions=ADDON_UPDATE_CONDITIONS,
on_condition=AddonsJobError,
)
async def update(self, slug: str, backup: Optional[bool] = False) -> None:
async def update(self, slug: str, backup: bool | None = False) -> None:
"""Update add-on."""
if slug not in self.local:
raise AddonsError(f"Add-on {slug} is not installed", _LOGGER.error)

View File

@@ -10,7 +10,7 @@ import secrets
import shutil
import tarfile
from tempfile import TemporaryDirectory
from typing import Any, Awaitable, Final, Optional
from typing import Any, Awaitable, Final
import aiohttp
from deepmerge import Merger
@@ -58,6 +58,7 @@ from ..docker.stats import DockerStats
from ..exceptions import (
AddonConfigurationError,
AddonsError,
AddonsJobError,
AddonsNotSupportedError,
ConfigurationFileError,
DockerError,
@@ -65,10 +66,18 @@ from ..exceptions import (
)
from ..hardware.data import Device
from ..homeassistant.const import WSEvent, WSType
from ..jobs.const import JobExecutionLimit
from ..jobs.decorator import Job
from ..utils import check_port
from ..utils.apparmor import adjust_profile
from ..utils.json import read_json_file, write_json_file
from .const import WATCHDOG_RETRY_SECONDS, AddonBackupMode
from .const import (
WATCHDOG_MAX_ATTEMPTS,
WATCHDOG_RETRY_SECONDS,
WATCHDOG_THROTTLE_MAX_CALLS,
WATCHDOG_THROTTLE_PERIOD,
AddonBackupMode,
)
from .model import AddonModel, Data
from .options import AddonOptions
from .utils import remove_data
@@ -103,6 +112,57 @@ class Addon(AddonModel):
super().__init__(coresys, slug)
self.instance: DockerAddon = DockerAddon(coresys, self)
self._state: AddonState = AddonState.UNKNOWN
self._manual_stop: bool = (
self.sys_hardware.helper.last_boot == self.sys_config.last_boot
)
@Job(
name=f"addon_{slug}_restart_after_problem",
limit=JobExecutionLimit.THROTTLE_RATE_LIMIT,
throttle_period=WATCHDOG_THROTTLE_PERIOD,
throttle_max_calls=WATCHDOG_THROTTLE_MAX_CALLS,
on_condition=AddonsJobError,
)
async def restart_after_problem(addon: Addon, state: ContainerState):
"""Restart unhealthy or failed addon."""
attempts = 0
while await addon.instance.current_state() == state:
if not addon.in_progress:
_LOGGER.warning(
"Watchdog found addon %s is %s, restarting...",
addon.name,
state.value,
)
try:
if state == ContainerState.FAILED:
# Ensure failed container is removed before attempting reanimation
if attempts == 0:
with suppress(DockerError):
await addon.instance.stop(remove_container=True)
await addon.start()
else:
await addon.restart()
except AddonsError as err:
attempts = attempts + 1
_LOGGER.error(
"Watchdog restart of addon %s failed!", addon.name
)
addon.sys_capture_exception(err)
else:
break
if attempts >= WATCHDOG_MAX_ATTEMPTS:
_LOGGER.critical(
"Watchdog cannot restart addon %s, failed all %s attempts",
addon.name,
attempts,
)
break
await asyncio.sleep(WATCHDOG_RETRY_SECONDS)
self._restart_after_problem = restart_after_problem
def __repr__(self) -> str:
"""Return internal representation."""
@@ -183,7 +243,7 @@ class Addon(AddonModel):
return self._available(self.data_store)
@property
def version(self) -> Optional[str]:
def version(self) -> str | None:
"""Return installed version."""
return self.persist[ATTR_VERSION]
@@ -207,7 +267,7 @@ class Addon(AddonModel):
)
@options.setter
def options(self, value: Optional[dict[str, Any]]) -> None:
def options(self, value: dict[str, Any] | None) -> None:
"""Store user add-on options."""
self.persist[ATTR_OPTIONS] = {} if value is None else deepcopy(value)
@@ -252,17 +312,17 @@ class Addon(AddonModel):
return self.persist[ATTR_UUID]
@property
def supervisor_token(self) -> Optional[str]:
def supervisor_token(self) -> str | None:
"""Return access token for Supervisor API."""
return self.persist.get(ATTR_ACCESS_TOKEN)
@property
def ingress_token(self) -> Optional[str]:
def ingress_token(self) -> str | None:
"""Return access token for Supervisor API."""
return self.persist.get(ATTR_INGRESS_TOKEN)
@property
def ingress_entry(self) -> Optional[str]:
def ingress_entry(self) -> str | None:
"""Return ingress external URL."""
if self.with_ingress:
return f"/api/hassio_ingress/{self.ingress_token}"
@@ -284,12 +344,12 @@ class Addon(AddonModel):
self.persist[ATTR_PROTECTED] = value
@property
def ports(self) -> Optional[dict[str, Optional[int]]]:
def ports(self) -> dict[str, int | None] | None:
"""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: dict[str, int | None] | None) -> None:
"""Set custom ports of add-on."""
if value is None:
self.persist.pop(ATTR_NETWORK, None)
@@ -304,7 +364,7 @@ class Addon(AddonModel):
self.persist[ATTR_NETWORK] = new_ports
@property
def ingress_url(self) -> Optional[str]:
def ingress_url(self) -> str | None:
"""Return URL to ingress url."""
if not self.with_ingress:
return None
@@ -315,7 +375,7 @@ class Addon(AddonModel):
return url
@property
def webui(self) -> Optional[str]:
def webui(self) -> str | None:
"""Return URL to webui or None."""
url = super().webui
if not url:
@@ -343,7 +403,7 @@ class Addon(AddonModel):
return f"{proto}://[HOST]:{port}{s_suffix}"
@property
def ingress_port(self) -> Optional[int]:
def ingress_port(self) -> int | None:
"""Return Ingress port."""
if not self.with_ingress:
return None
@@ -354,7 +414,7 @@ class Addon(AddonModel):
return port
@property
def ingress_panel(self) -> Optional[bool]:
def ingress_panel(self) -> bool | None:
"""Return True if the add-on access support ingress."""
if not self.with_ingress:
return None
@@ -367,19 +427,19 @@ class Addon(AddonModel):
self.persist[ATTR_INGRESS_PANEL] = value
@property
def audio_output(self) -> Optional[str]:
def audio_output(self) -> str | None:
"""Return a pulse profile for output or None."""
if not self.with_audio:
return None
return self.persist.get(ATTR_AUDIO_OUTPUT)
@audio_output.setter
def audio_output(self, value: Optional[str]):
def audio_output(self, value: str | None):
"""Set audio output profile settings."""
self.persist[ATTR_AUDIO_OUTPUT] = value
@property
def audio_input(self) -> Optional[str]:
def audio_input(self) -> str | None:
"""Return pulse profile for input or None."""
if not self.with_audio:
return None
@@ -387,12 +447,12 @@ class Addon(AddonModel):
return self.persist.get(ATTR_AUDIO_INPUT)
@audio_input.setter
def audio_input(self, value: Optional[str]) -> None:
def audio_input(self, value: str | None) -> None:
"""Set audio input settings."""
self.persist[ATTR_AUDIO_INPUT] = value
@property
def image(self) -> Optional[str]:
def image(self) -> str | None:
"""Return image name of add-on."""
return self.persist.get(ATTR_IMAGE)
@@ -625,6 +685,7 @@ class Addon(AddonModel):
async def stop(self) -> None:
"""Stop add-on."""
self._manual_stop = True
try:
await self.instance.stop()
except DockerError as err:
@@ -893,6 +954,7 @@ class Addon(AddonModel):
ContainerState.HEALTHY,
ContainerState.UNHEALTHY,
]:
self._manual_stop = False
self.state = AddonState.STARTED
elif event.state == ContainerState.STOPPED:
self.state = AddonState.STOPPED
@@ -901,43 +963,16 @@ class Addon(AddonModel):
async def watchdog_container(self, event: DockerContainerStateEvent) -> None:
"""Process state changes in addon container and restart if necessary."""
if not (event.name == self.instance.name and self.watchdog):
if event.name != self.instance.name:
return
if event.state == ContainerState.UNHEALTHY:
while await self.instance.current_state() == event.state:
if not self.in_progress:
_LOGGER.warning(
"Watchdog found addon %s is unhealthy, restarting...", self.name
)
try:
await self.restart()
except AddonsError as err:
_LOGGER.error("Watchdog restart of addon %s failed!", self.name)
self.sys_capture_exception(err)
else:
break
# Skip watchdog if not enabled or manual stopped
if not self.watchdog or self._manual_stop:
return
await asyncio.sleep(WATCHDOG_RETRY_SECONDS)
elif event.state == ContainerState.FAILED:
# Ensure failed container is removed before attempting reanimation
with suppress(DockerError):
await self.instance.stop(remove_container=True)
while await self.instance.current_state() == event.state:
if not self.in_progress:
_LOGGER.warning(
"Watchdog found addon %s failed, restarting...", self.name
)
try:
await self.start()
except AddonsError as err:
_LOGGER.error(
"Watchdog reanimation of addon %s failed!", self.name
)
self.sys_capture_exception(err)
else:
break
await asyncio.sleep(WATCHDOG_RETRY_SECONDS)
if event.state in [
ContainerState.FAILED,
ContainerState.STOPPED,
ContainerState.UNHEALTHY,
]:
await self._restart_after_problem(self, event.state)

View File

@@ -15,6 +15,7 @@ from ..const import (
META_ADDON,
)
from ..coresys import CoreSys, CoreSysAttributes
from ..docker.interface import MAP_ARCH
from ..exceptions import ConfigurationFileError
from ..utils.common import FileConfiguration, find_one_filetype
from .validate import SCHEMA_BUILD_CONFIG
@@ -50,6 +51,9 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
if not self._data[ATTR_BUILD_FROM]:
return f"ghcr.io/home-assistant/{self.sys_arch.default}-base:latest"
if isinstance(self._data[ATTR_BUILD_FROM], str):
return self._data[ATTR_BUILD_FROM]
# Evaluate correct base image
arch = self.sys_arch.match(list(self._data[ATTR_BUILD_FROM].keys()))
return self._data[ATTR_BUILD_FROM][arch]
@@ -87,6 +91,7 @@ class AddonBuild(FileConfiguration, CoreSysAttributes):
"pull": True,
"forcerm": not self.sys_dev,
"squash": self.squash,
"platform": MAP_ARCH[self.sys_arch.match(self.addon.arch)],
"labels": {
"io.hass.version": version,
"io.hass.arch": self.sys_arch.default,

View File

@@ -1,6 +1,9 @@
"""Add-on static data."""
from datetime import timedelta
from enum import Enum
from ..jobs.const import JobCondition
class AddonBackupMode(str, Enum):
"""Backup mode of an Add-on."""
@@ -12,3 +15,14 @@ class AddonBackupMode(str, Enum):
ATTR_BACKUP = "backup"
ATTR_CODENOTARY = "codenotary"
WATCHDOG_RETRY_SECONDS = 10
WATCHDOG_MAX_ATTEMPTS = 5
WATCHDOG_THROTTLE_PERIOD = timedelta(minutes=30)
WATCHDOG_THROTTLE_MAX_CALLS = 10
ADDON_UPDATE_CONDITIONS = [
JobCondition.FREE_SPACE,
JobCondition.HEALTHY,
JobCondition.INTERNET_HOST,
JobCondition.PLUGINS_UPDATED,
JobCondition.SUPERVISOR_UPDATED,
]

View File

@@ -1,12 +1,10 @@
"""Init file for Supervisor add-ons."""
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, Awaitable, Optional
from typing import Any, Awaitable
from awesomeversion import AwesomeVersion, AwesomeVersionException
from supervisor.addons.const import AddonBackupMode
from ..const import (
ATTR_ADVANCED,
ATTR_APPARMOR,
@@ -79,7 +77,7 @@ from ..const import (
)
from ..coresys import CoreSys, CoreSysAttributes
from ..docker.const import Capabilities
from .const import ATTR_BACKUP, ATTR_CODENOTARY
from .const import ATTR_BACKUP, ATTR_CODENOTARY, AddonBackupMode
from .options import AddonOptions, UiOptions
from .validate import RE_SERVICE, RE_VOLUME
@@ -125,7 +123,7 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_BOOT]
@property
def auto_update(self) -> Optional[bool]:
def auto_update(self) -> bool | None:
"""Return if auto update is enable."""
return None
@@ -150,22 +148,22 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_TIMEOUT]
@property
def uuid(self) -> Optional[str]:
def uuid(self) -> str | None:
"""Return an API token for this add-on."""
return None
@property
def supervisor_token(self) -> Optional[str]:
def supervisor_token(self) -> str | None:
"""Return access token for Supervisor API."""
return None
@property
def ingress_token(self) -> Optional[str]:
def ingress_token(self) -> str | None:
"""Return access token for Supervisor API."""
return None
@property
def ingress_entry(self) -> Optional[str]:
def ingress_entry(self) -> str | None:
"""Return ingress external URL."""
return None
@@ -175,7 +173,7 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_DESCRIPTON]
@property
def long_description(self) -> Optional[str]:
def long_description(self) -> str | None:
"""Return README.md as long_description."""
readme = Path(self.path_location, "README.md")
@@ -245,32 +243,32 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data.get(ATTR_DISCOVERY, [])
@property
def ports_description(self) -> Optional[dict[str, str]]:
def ports_description(self) -> dict[str, str] | None:
"""Return descriptions of ports."""
return self.data.get(ATTR_PORTS_DESCRIPTION)
@property
def ports(self) -> Optional[dict[str, Optional[int]]]:
def ports(self) -> dict[str, int | None] | None:
"""Return ports of add-on."""
return self.data.get(ATTR_PORTS)
@property
def ingress_url(self) -> Optional[str]:
def ingress_url(self) -> str | None:
"""Return URL to ingress url."""
return None
@property
def webui(self) -> Optional[str]:
def webui(self) -> str | None:
"""Return URL to webui or None."""
return self.data.get(ATTR_WEBUI)
@property
def watchdog(self) -> Optional[str]:
def watchdog(self) -> str | None:
"""Return URL to for watchdog or None."""
return self.data.get(ATTR_WATCHDOG)
@property
def ingress_port(self) -> Optional[int]:
def ingress_port(self) -> int | None:
"""Return Ingress port."""
return None
@@ -315,7 +313,7 @@ class AddonModel(CoreSysAttributes, ABC):
return [Path(node) for node in self.data.get(ATTR_DEVICES, [])]
@property
def environment(self) -> Optional[dict[str, str]]:
def environment(self) -> dict[str, str] | None:
"""Return environment of add-on."""
return self.data.get(ATTR_ENVIRONMENT)
@@ -364,12 +362,12 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data.get(ATTR_BACKUP_EXCLUDE, [])
@property
def backup_pre(self) -> Optional[str]:
def backup_pre(self) -> str | None:
"""Return pre-backup command."""
return self.data.get(ATTR_BACKUP_PRE)
@property
def backup_post(self) -> Optional[str]:
def backup_post(self) -> str | None:
"""Return post-backup command."""
return self.data.get(ATTR_BACKUP_POST)
@@ -394,7 +392,7 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_INGRESS]
@property
def ingress_panel(self) -> Optional[bool]:
def ingress_panel(self) -> bool | None:
"""Return True if the add-on access support ingress."""
return None
@@ -444,7 +442,7 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_DEVICETREE]
@property
def with_tmpfs(self) -> Optional[str]:
def with_tmpfs(self) -> str | None:
"""Return if tmp is in memory of add-on."""
return self.data[ATTR_TMPFS]
@@ -464,12 +462,12 @@ class AddonModel(CoreSysAttributes, ABC):
return self.data[ATTR_VIDEO]
@property
def homeassistant_version(self) -> Optional[str]:
def homeassistant_version(self) -> str | None:
"""Return min Home Assistant version they needed by Add-on."""
return self.data.get(ATTR_HOMEASSISTANT)
@property
def url(self) -> Optional[str]:
def url(self) -> str | None:
"""Return URL of add-on."""
return self.data.get(ATTR_URL)
@@ -512,7 +510,7 @@ class AddonModel(CoreSysAttributes, ABC):
return self.sys_arch.default
@property
def image(self) -> Optional[str]:
def image(self) -> str | None:
"""Generate image name from data."""
return self._image(self.data)
@@ -573,7 +571,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) -> list[dict[any, any]] | None:
"""Create a UI schema for add-on options."""
raw_schema = self.data[ATTR_SCHEMA]
@@ -592,7 +590,7 @@ class AddonModel(CoreSysAttributes, ABC):
return ATTR_CODENOTARY in self.data
@property
def codenotary(self) -> Optional[str]:
def codenotary(self) -> str | None:
"""Return Signer email address for CAS."""
return self.data.get(ATTR_CODENOTARY)
@@ -616,7 +614,7 @@ class AddonModel(CoreSysAttributes, ABC):
return False
# Home Assistant
version: Optional[AwesomeVersion] = config.get(ATTR_HOMEASSISTANT)
version: AwesomeVersion | None = config.get(ATTR_HOMEASSISTANT)
try:
return self.sys_homeassistant.version >= version
except (AwesomeVersionException, TypeError):
@@ -640,7 +638,7 @@ class AddonModel(CoreSysAttributes, ABC):
"""Uninstall this add-on."""
return self.sys_addons.uninstall(self.slug)
def update(self, backup: Optional[bool] = False) -> Awaitable[None]:
def update(self, backup: bool | None = False) -> Awaitable[None]:
"""Update this add-on."""
return self.sys_addons.update(self.slug, backup=backup)

View File

@@ -3,7 +3,7 @@ import hashlib
import logging
from pathlib import Path
import re
from typing import Any, Union
from typing import Any
import voluptuous as vol
@@ -293,7 +293,7 @@ class UiOptions(CoreSysAttributes):
multiple: bool = False,
) -> None:
"""Validate a single element."""
ui_node: dict[str, Union[str, bool, float, list[str]]] = {"name": key}
ui_node: dict[str, str | bool | float | list[str]] = {"name": key}
# If multiple
if multiple:

View File

@@ -7,8 +7,6 @@ import uuid
import voluptuous as vol
from supervisor.addons.const import AddonBackupMode
from ..const import (
ARCH_ALL,
ATTR_ACCESS_TOKEN,
@@ -110,7 +108,7 @@ from ..validate import (
uuid_match,
version_tag,
)
from .const import ATTR_BACKUP, ATTR_CODENOTARY
from .const import ATTR_BACKUP, ATTR_CODENOTARY, AddonBackupMode
from .options import RE_SCHEMA_ELEMENT
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -353,8 +351,9 @@ SCHEMA_ADDON_CONFIG = vol.All(
# pylint: disable=no-value-for-parameter
SCHEMA_BUILD_CONFIG = vol.Schema(
{
vol.Optional(ATTR_BUILD_FROM, default=dict): vol.Schema(
{vol.In(ARCH_ALL): vol.Match(RE_DOCKER_IMAGE_BUILD)}
vol.Optional(ATTR_BUILD_FROM, default=dict): vol.Any(
vol.Match(RE_DOCKER_IMAGE_BUILD),
vol.Schema({vol.In(ARCH_ALL): vol.Match(RE_DOCKER_IMAGE_BUILD)}),
),
vol.Optional(ATTR_SQUASH, default=False): vol.Boolean(),
vol.Optional(ATTR_ARGS, default=dict): vol.Schema({str: str}),

View File

@@ -1,15 +1,13 @@
"""Init file for Supervisor RESTful API."""
import logging
from pathlib import Path
from typing import Any, Optional
from typing import Any
from aiohttp import web
from supervisor.api.utils import api_process
from supervisor.const import AddonState
from supervisor.exceptions import APIAddonNotInstalled
from ..const import AddonState
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import APIAddonNotInstalled
from .addons import APIAddons
from .audio import APIAudio
from .auth import APIAuth
@@ -35,6 +33,7 @@ from .security import APISecurity
from .services import APIServices
from .store import APIStore
from .supervisor import APISupervisor
from .utils import api_process
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -64,7 +63,7 @@ class RestAPI(CoreSysAttributes):
# service stuff
self._runner: web.AppRunner = web.AppRunner(self.webapp)
self._site: Optional[web.TCPSite] = None
self._site: web.TCPSite | None = None
async def load(self) -> None:
"""Register REST API Calls."""
@@ -278,6 +277,10 @@ class RestAPI(CoreSysAttributes):
"/resolution/issue/{issue}",
api_resolution.dismiss_issue,
),
web.get(
"/resolution/issue/{issue}/suggestions",
api_resolution.suggestions_for_issue,
),
web.post("/resolution/healthcheck", api_resolution.healthcheck),
]
)

View File

@@ -2,7 +2,7 @@
import asyncio
from ipaddress import ip_address
import logging
from typing import Any, Union
from typing import Any
import aiohttp
from aiohttp import ClientTimeout, hdrs, web
@@ -86,7 +86,7 @@ class APIIngress(CoreSysAttributes):
@require_home_assistant
async def handler(
self, request: web.Request
) -> Union[web.Response, web.StreamResponse, web.WebSocketResponse]:
) -> web.Response | web.StreamResponse | web.WebSocketResponse:
"""Route data to Supervisor ingress service."""
# Check Ingress Session
@@ -157,7 +157,7 @@ class APIIngress(CoreSysAttributes):
async def _handle_request(
self, request: web.Request, addon: Addon, path: str
) -> Union[web.Response, web.StreamResponse]:
) -> web.Response | web.StreamResponse:
"""Ingress route for request."""
url = self._create_url(addon, path)
source_header = _init_header(request, addon)
@@ -216,9 +216,7 @@ class APIIngress(CoreSysAttributes):
return response
def _init_header(
request: web.Request, addon: str
) -> Union[CIMultiDict, dict[str, str]]:
def _init_header(request: web.Request, addon: str) -> CIMultiDict | dict[str, str]:
"""Create initial header."""
headers = {}

View File

@@ -1,14 +1,14 @@
function loadES5() {
var el = document.createElement('script');
el.src = '/api/hassio/app/frontend_es5/entrypoint.75b60951.js';
el.src = '/api/hassio/app/frontend_es5/entrypoint.169d7fb4.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.f358ba39.js')")();
new Function("import('/api/hassio/app/frontend_latest/entrypoint.24687610.js')")();
} catch (err) {
loadES5();
}

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

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
{
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.75b60951.js"
"entrypoint.js": "/api/hassio/app/frontend_es5/entrypoint.169d7fb4.js"
}

View File

@@ -912,4 +912,4 @@
}
}
`]}},{kind:"method",key:"firstUpdated",value:async function(){if(""===this.route.path){const e=(0,d.io)("addon"),t=(0,d.io)("repository_url");if(t){if(!(await(0,h.Er)(this.hass)).repositories.find((e=>e.source===t))){if(!await(0,p.g7)(this,{title:this.supervisor.localize("my.add_addon_repository_title"),text:this.supervisor.localize("my.add_addon_repository_description",{addon:e,repository:t}),confirmText:this.supervisor.localize("common.add"),dismissText:this.supervisor.localize("common.cancel")}))return void(this._error=this.supervisor.localize("my.error_repository_not_found"));try{await(0,h.FV)(this.hass,t)}catch(e){this._error=(0,c.js)(e)}}}if(e){const t=(await(0,h.Er)(this.hass)).addons.some((t=>t.slug===e));t?(0,a.c)(`/hassio/addon/${e}`,{replace:!0}):this._error=this.supervisor.localize("my.error_addon_not_found")}}this.addEventListener("hass-api-called",(e=>this._apiCalled(e)))}},{kind:"method",key:"_apiCalled",value:async function(e){var t;if(!e.detail.success)return;const r=null===(t=e.detail.path)||void 0===t?void 0:t.split("/");if(!r||0===r.length)return;const i=r[r.length-1];["uninstall","install","update","start","stop"].includes(i)&&(0,s.B)(this,"supervisor-collection-refresh",{collection:"addon"}),"uninstall"===i?window.history.back():"install"===i?this.addon=await(0,l.AD)(this.hass,this.addon.slug):await this._routeDataChanged()}},{kind:"method",key:"updated",value:function(e){e.has("route")&&!this.addon&&this._routeDataChanged()}},{kind:"method",key:"_routeDataChanged",value:async function(){const e=this.route.path.split("/")[1];if(e)try{if(!this.supervisor.addon){const e=await(0,l.yt)(this.hass);(0,s.B)(this,"supervisor-update",{addon:e})}this.addon=await(0,l.R_)(this.hass,this.supervisor,e)}catch(e){this._error=`Error fetching addon info: ${(0,c.js)(e)}`,this.addon=void 0}}}]}}),i.oi)}}]);
//# sourceMappingURL=29a9c475.js.map
//# sourceMappingURL=139ce644.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -98,4 +98,4 @@
margin-top: 12px;
}
`]}}]}}),i.oi)},7628:(e,r,t)=>{t.r(r),t.d(r,{dump:()=>i.$w});var i=t(7426)}}]);
//# sourceMappingURL=faa8eea5.js.map
//# sourceMappingURL=50e67b8b.js.map

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

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.

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

File diff suppressed because one or more lines are too long

View File

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

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