Compare commits

..

168 Commits

Author SHA1 Message Date
Stefan Agner
747b4152bd Log API exceptions on call site
Since make_request is no longer logging API exceptions on its own, we
need to log them where we call make_request. This approach gives the
user more context about what Supervisor was trying to do when the error
happened.
2025-09-25 16:08:36 +02:00
Stefan Agner
3634f9f301 Remove check_port from API checks
Remove check_port usage from Home Assistant API connection checks.
Simply rely on errors raised from actual connection attempts. During a
Supervisor startup when Home Assistant Core is running (e.g. after a
Supervisor update) we make about 10 successful API checks. The old code
path did a port check and then a connection check, causing two socket
creation. The new code without the separate port check safes 10 socket
creations per startup (the aiohttp connections are reused, hence do not
cause only one socket creation).
2025-09-25 15:41:11 +02:00
Stefan Agner
e5e62aca46 Simplify ensure_access_token
Make the caller of ensure_access_token responsible for connection error
handling. This is especially useful for API connection checks, as it
avoids an extra call to the API (if we fail to connect when refreshing
the token there is no point in calling the API to check if it is up).
Document the change in the docstring.

Also avoid the overhead of creating a Job object. We can simply use an
asyncio.Lock() to ensure only one coroutine is refreshing the token at
a time. This also avoids Job interference in Exception handling.
2025-09-25 15:19:58 +02:00
Stefan Agner
fabfe760fb Fix flaky test_get_dir_structure_sizes_ebadmsg_error (#6211) 2025-09-25 12:29:18 +02:00
dependabot[bot]
092013e457 Bump home-assistant/wheels from 2025.07.0 to 2025.09.0 (#6209)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 10:43:28 +02:00
dependabot[bot]
e13f216b2e Bump actions/cache from 4.2.4 to 4.3.0 (#6208)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 10:42:51 +02:00
Stefan Agner
97c7686b95 Simplify API validation by removing confusing origin parameter (#6203)
Co-authored-by: Claude <noreply@anthropic.com>
2025-09-23 22:10:48 +02:00
Mike Degatano
42f93d0176 Remove message_template field from errors (#6205) 2025-09-23 17:07:38 +02:00
Stefan Agner
ed7155604c Fix range header to correctly fetch latest logs (#6202)
* Fix range header to correctly fetch latest logs

Add a colon before line numbers to indicate that no cursor is used.
This makes the range header work when fetching latest logs from
systemd-journal-gatewayd.

* Fix pytest
2025-09-23 16:43:20 +02:00
Stefan Agner
595e33ac68 Bump cosign to v2.5.3 (#6204)
Follow the builder bump of 2025.09.0 and use cosign v2.5.3 for
Supervisor too.
2025-09-23 16:43:03 +02:00
dependabot[bot]
ae70ffd1b2 Bump getsentry/action-release from 3.2.0 to 3.3.0 (#6201)
Bumps [getsentry/action-release](https://github.com/getsentry/action-release) from 3.2.0 to 3.3.0.
- [Release notes](https://github.com/getsentry/action-release/releases)
- [Changelog](https://github.com/getsentry/action-release/blob/master/CHANGELOG.md)
- [Commits](526942b682...4f502acc1d)

---
updated-dependencies:
- dependency-name: getsentry/action-release
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-23 15:40:58 +02:00
dependabot[bot]
17cb18a371 Bump coverage from 7.10.6 to 7.10.7 (#6200)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-22 13:17:59 +02:00
Mike Degatano
9f5bebd0eb Mount falls back on restart if reload fails (#6197) 2025-09-19 17:59:50 +02:00
Stefan Agner
c712d3cc53 Check Core version and raise unsupported if older than 2 years (#6148)
* Check Core version and raise unsupported if older than 2 years

Check the currently installed Core version relative to the current
date, and if its older than 2 years, mark the system unsupported.
Also add a Job condition to prevent automatic refreshing of the update
information in this case.

* Handle landing page correctly

* Handle non-parseable versions gracefully

Also align handling between OS and Core version evaluations.

* Extend and fix test coverage

* Improve Job condition error

* Fix pytest

* Block execution of fetch_data and store reload jobs

Block execution of fetch_data and store reload jobs if the core version
is unsupported. This essentially freezes the installation until the
user takes action and updates the Core version to a supported one.

* Use latest known Core version as reference

Instead of using current date to determine if Core version is more than
2 years old, use the latest known Core version as reference point and
check if current version is more than 24 releases behind.

This is crucial because when update information refresh is disabled due to
unsupported Core version, using date would create a permanent unsupported
state. Even if users update to the last known version in 4+ years, the
system would remain unsupported. By using latest known version as reference,
updating Core to the last known version makes the system supported again,
allowing update information refresh to resume.

This ensures users can always escape the unsupported state by updating
to the last known Core version, maintaining the update refresh cycle.

* Improve version comparision logic

* Use Home Assistant Core instead of just Core

Avoid any ambiguity in what is exactly outdated/unsupported by using
Home Assistant Core instead of just Core.

* Sort const alphabetically

* Update tests/resolution/evaluation/test_evaluate_home_assistant_core_version.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-19 17:58:37 +02:00
dependabot[bot]
46fc5c8aa1 Bump ruff from 0.13.0 to 0.13.1 (#6199)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.13.0 to 0.13.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.13.0...0.13.1)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-19 13:01:34 +02:00
dependabot[bot]
8b23383e26 Bump mypy from 1.18.1 to 1.18.2 (#6198)
Bumps [mypy](https://github.com/python/mypy) from 1.18.1 to 1.18.2.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.18.1...v1.18.2)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-19 13:01:19 +02:00
dependabot[bot]
c1ccb00946 Bump debugpy from 1.8.16 to 1.8.17 (#6196)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.8.16 to 1.8.17.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.8.16...v1.8.17)

---
updated-dependencies:
- dependency-name: debugpy
  dependency-version: 1.8.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-18 10:40:31 +02:00
dependabot[bot]
5693a5be0d Bump cryptography from 45.0.7 to 46.0.1 (#6194)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-17 09:32:47 +02:00
Mike Degatano
01911a44cd Persistent notifications to repairs and fix free_space check (#6179)
* Persistent notifications to repairs and fix free_space check

* Fix tests mocking too little free space
2025-09-16 11:22:59 -04:00
Lukas Waslowski
857dae7736 Allow adding translations for nested fields to support home-assistant/frontend#26997 (#6180) 2025-09-16 11:36:45 +02:00
Stefan Agner
d2ddd9579c Cancel Supervisor startup task on early shutdown signal (#6175)
When receiving a shutdown signal during startup, the Supervisor should
cancel its startup task to ensure a graceful shutdown. This prevents
Supervisor accidentally accessing the Event loop after it has been
closed by the stop procedure:

RuntimeError: Event loop stopped before Future completed.
2025-09-16 11:32:43 +02:00
Lukas Waslowski
ac9947d599 Allow deeply nested dicts and lists in addon config schemas (#6171)
* Allow arbitrarily nested addon config schemas

* Disallow lists directly nested in another list in addon schema

* Handle arbitrarily nested addon schemas in UiOptions class

* Handle arbitrarily nested addon schemas in AddonOptions class

* Add tests for addon config schemas

* Add tests for addon option validation
2025-09-16 11:32:28 +02:00
Jan Čermák
2e22e1e884 Add endpoint for complete logs of the latest container startup (#6163)
* Add endpoint for complete logs of the latest container startup

Add endpoint that returns complete logs of the latest startup of
container, which can be used for downloading Core logs in the frontend.

Realtime filtering header is used for the Journal API and StartedAt
parameter from the Docker API is used as the reference point. This means
that any other Range header is ignored for this parameter, yet the
"lines" query argument can be used to limit the number of lines. By
default "infinite" number of lines is returned.

Closes #6147

* Implement fallback for latest logs for OS older than 16.0

Implement fallback which uses the internal CONTAINER_LOG_EPOCH metadata
added to logs created by the Docker logger. Still prefer the time-based
method, as it has lower overhead and using public APIs.

* Address review comments

* Only use CONTAINER_LOG_EPOCH for latest logs

As pointed out in the review comments, we might not be able to get the
StartedAt for add-ons that are not running. Thus we need to use the only
reliable mechanism available now, which is the container log epoch.

* Remove dead code for 'Range: realtime' header handling
2025-09-16 11:29:28 +02:00
dependabot[bot]
e7f3573e32 Bump sentry-sdk from 2.37.1 to 2.38.0 (#6192)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 10:00:50 +02:00
dependabot[bot]
b26451a59a Bump types-docker from 7.1.0.20250907 to 7.1.0.20250916 (#6191)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-16 09:57:24 +02:00
dependabot[bot]
4e882f7c76 Bump home-assistant/builder from 2025.03.0 to 2025.09.0 (#6190) 2025-09-16 09:06:10 +02:00
dependabot[bot]
5fa50ccf05 Bump types-pyyaml from 6.0.12.20250822 to 6.0.12.20250915 (#6189)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 11:36:09 +02:00
dependabot[bot]
3891df5266 Bump sigstore/cosign-installer from 3.9.2 to 3.10.0 (#6188)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-15 09:44:35 +02:00
dependabot[bot]
5aad32c15b Bump types-requests from 2.32.4.20250809 to 2.32.4.20250913 (#6187) 2025-09-15 08:36:51 +02:00
Simon Lamon
4a40490af7 Pin SHA for all Github Actions (#6186) 2025-09-14 17:54:02 +02:00
Stefan Agner
0a46e030f5 Bump minimal Docker to 24.0.0 (#6178)
* Bump minimal Docker to 23.0.0

Home Assistant OS 10.0 update to Docker 23.0.3, lets make this
Docker version the minimum we support. This will allow us to use
zstd compression for layers (see https://github.com/home-assistant/builder/pull/245).

* Bump minimal Docker version to 24.0.0
2025-09-12 15:00:59 +02:00
dependabot[bot]
bd00f90304 Bump mypy from 1.17.1 to 1.18.1 (#6184)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-12 09:39:13 +02:00
dependabot[bot]
819f097f01 Bump ruff from 0.12.12 to 0.13.0 (#6181)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.12.12 to 0.13.0.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.12.12...0.13.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-11 09:08:07 +02:00
dependabot[bot]
4513592993 Bump sentry-sdk from 2.37.0 to 2.37.1 (#6177)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.37.0 to 2.37.1.
- [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/2.37.0...2.37.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-10 09:00:16 +02:00
dependabot[bot]
7e526a26af Bump pytest-cov from 6.3.0 to 7.0.0 (#6176)
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 6.3.0 to 7.0.0.
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v6.3.0...v7.0.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-10 09:00:09 +02:00
Petar Petrov
b3af22f048 Ignore missing files when counting used space (#6174) 2025-09-09 13:39:06 +02:00
Jan Čermák
bbb9469c1c Write cidfiles of Docker containers and mount them individually to /run/cid (#6154)
* Write cidfiles of Docker containers and mount them individually to /run/cid

There is no standard way to get the container ID in the container
itself, which can be needed for instance for #6006. The usual pattern is
to use the --cidfile argument of Docker CLI and mount the generated file
to the container. However, this is feature of Docker CLI and we can't
use it when creating the containers via API. To get container ID to
implement native logging in e.g. Core as well, we need the help of the
Supervisor.

This change implements similar feature fully in Supervisor's DockerAPI
class that orchestrates lifetime of all containers managed by
Supervisor. The files are created in the SUPERVISOR_DATA directory, as
it needs to be persisted between reboots, just as the instances of
Docker containers are.

Supervisor's cidfile must be created when starting the Supervisor
itself, for that see home-assistant/operating-system#4276.

* Address review comments, fix mounting of the cidfile
2025-09-09 13:38:31 +02:00
Stefan Agner
859c32a706 Drop unused coud backup dir constant (#6172)
The constant PATH_CLOUD_BACKUP is not used anywhere in the codebase.
Remove it to clean up the code. This is a leftover from a removed
initial cloud backup support implementation and got missed in #5464.
2025-09-08 15:04:24 -04:00
Stefan Agner
87fc84c65c Avoid setup failure on missing timedatectl (#6169)
When timedatectl is not available (e.g. in minimal devcontainers),
the code currently fails to setup due to missing timedate service on
D-Bus. This change makes the code more robust by checking only checking
for the presence of the service if we actually going to use it.
2025-09-08 11:29:44 +02:00
dependabot[bot]
e38ca5acb4 Bump pytest-cov from 6.2.1 to 6.3.0 (#6167)
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 6.2.1 to 6.3.0.
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v6.2.1...v6.3.0)

---
updated-dependencies:
- dependency-name: pytest-cov
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 09:28:31 +02:00
dependabot[bot]
09cd8eede2 Bump types-docker from 7.1.0.20250822 to 7.1.0.20250907 (#6166)
Bumps [types-docker](https://github.com/typeshed-internal/stub_uploader) from 7.1.0.20250822 to 7.1.0.20250907.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-docker
  dependency-version: 7.1.0.20250907
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 09:28:24 +02:00
dependabot[bot]
d1c537b280 Bump sentry-sdk from 2.36.0 to 2.37.0 (#6168)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.36.0 to 2.37.0.
- [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/2.36.0...2.37.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-08 09:28:15 +02:00
dependabot[bot]
e6785d6a89 Bump ruff from 0.12.11 to 0.12.12 (#6161)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.12.11 to 0.12.12.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.12.11...0.12.12)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.12.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 10:31:12 +02:00
Stefan Agner
59e051ad93 Avoid duplicate evaluate_system() calls during resolution manager setup (#6133)
* Avoid duplicate evaluate_system() calls during resolution manager setup

During resolution manager initialization, both the initial healthcheck call
and the subsequent setup() call would trigger evaluate_system(), causing
redundant system evaluation. All following calls in healthcheck() are
already suppressed during the setup stage, we can optimize this by
calling check_system() directly during load() instead of the full
healthcheck().

This reduces unnecessary processing during supervisor startup while
maintaining the same functional behavior.

* Call full healthcheck on setup and move diagnostics to core start

The OS Agent diagnostics if statement accesses OS Agent through D-Bus
already. This makes the exception handling inside the if statement
not really useful.

Move OS Agent diagnostics setting to core start so we can leverage
the existing global Exception handling in start() instead of
having to add another try/except block in setup(). It also covers the
if statement itself.
2025-09-05 10:16:06 +02:00
dependabot[bot]
3397def8b9 Bump actions/github-script from 7 to 8 (#6158)
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 09:55:13 +02:00
dependabot[bot]
b832edc10d Bump codecov/codecov-action from 5.5.0 to 5.5.1 (#6159)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.0 to 5.5.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.5.0...v5.5.1)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 5.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 09:54:33 +02:00
dependabot[bot]
f69071878c Bump sentry-sdk from 2.35.2 to 2.36.0 (#6162)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.35.2 to 2.36.0.
- [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/2.35.2...2.36.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 09:54:09 +02:00
dependabot[bot]
e065ba6081 Bump pytest from 8.4.1 to 8.4.2 (#6160)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.1 to 8.4.2.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.1...8.4.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-05 09:52:53 +02:00
dependabot[bot]
38611ad12f Bump actions/stale from 9.1.0 to 10.0.0 (#6155)
Bumps [actions/stale](https://github.com/actions/stale) from 9.1.0 to 10.0.0.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v9.1.0...v10.0.0)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-version: 10.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-04 14:40:56 +02:00
dependabot[bot]
8beb66d46c Bump actions/setup-python from 5.6.0 to 6.0.0 (#6156)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.6.0 to 6.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5.6.0...v6.0.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-04 14:40:42 +02:00
Stefan Agner
c277f3cad6 Store and persist OS upgrade map to fix update path evaluation (#6152)
* Store and persist OS upgrade map to fix update path evaluation

The existing logic calculated OS upgrade paths inline during fetch_data,
which will not get reevaluted when the current OS is unsupported
(JobCondition.OS_SUPPORTED). E.g. after updating from 11.4 to 11.5, the
system wouldn't offer the next available update (15.2) because the
upgrade path calculation relied on fresh data from the blocked fetch
operation.

Changes:
- Add ATTR_HASSOS_UPGRADE constant and schema validation
- Store hassos-upgrade map from version JSON in updater data
- Refactor version_hassos property to use stored upgrade map instead of
  inline calculation during fetch_data
- Maintain upgrade path logic: upgrade within major version first, then
  jump to next major version when at the latest in current major
- Add type safety checks for version.major access

This ensures upgrade paths work correctly even when update data refresh
is blocked due to unsupported OS versions, fixing the scenario where
HAOS 11.5 wouldn't show 15.2 as the next available update.

* Update supervisor/updater.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Address mypy issue

* Fix pytest

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-04 13:19:31 +02:00
Igor Yamolov
236c39cbb0 Add network interface settings for mDNS/LLMNR (#5520) 2025-09-04 13:18:11 +02:00
Mike Degatano
7ed83a15fe Add availability API for addons (#6140)
* Add availability API for addons

* Add cast back and test for latest version of installed addon

* Make error responses more translation/client library friendly

* Add test cases for install/update APIs
2025-09-04 11:14:42 +02:00
Felipe Santos
a3a5f6ba98 Fix WebSocket proxy for add-ons not forwarding ping/pong frame data (#6144)
* Fix proxied add-on WebSocket connections closing after 40 seconds

* Undo autoping=True

* Add debug logging for ping/pong frames

* Foward ping and pong msg.data too

* Add temporary workaround for devcontainer issue

* Forward 8000 through docker devcontainer

* Remove debug code
2025-09-04 11:12:42 +02:00
Stefan Agner
8d3ededf2f Update NM developer page URL (#6151)
* Update NM developer page URL

* Update remaining NetworkManager URLs to new location
2025-09-04 11:02:34 +02:00
Jan Čermák
3d62c9afb1 Make test_job_decorator tests timezone agnostic (#6153)
Running tests in UTC+2 timezone makes some of the tests fail because the
mocked time in the future is actually in the past, as UTC is used as the
new reference point. Adjust the tests to mock also the time when the
first execution of function happens.

Instances where the second execution happened "immediately" were mocked
to happen 1ms later. The 1ms delta is also needed to be added when
mocking time 1h in the future, otherwise it will be throttled too.
2025-09-03 17:55:28 +02:00
dependabot[bot]
ef313d1fb5 Bump sentry-sdk from 2.35.1 to 2.35.2 (#6150)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.35.1 to 2.35.2.
- [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/2.35.1...2.35.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 13:50:48 +02:00
dependabot[bot]
cae31637ae Bump cryptography from 45.0.6 to 45.0.7 (#6149)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 11:21:12 +02:00
Mike Degatano
9392d10625 Add background option to update/install APIs (#6134)
* Add background option to update/install APIs

* Refactor to use common background_task utility in backups too

* Use a validation_complete event rather then looking for bus events
2025-09-03 08:33:00 +02:00
dependabot[bot]
5ce62f324f Bump coverage from 7.10.5 to 7.10.6 (#6145)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-01 14:05:57 +02:00
dependabot[bot]
f84d514958 Bump ruff from 0.12.10 to 0.12.11 (#6141)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-29 11:15:52 +02:00
dependabot[bot]
3c39f2f785 Bump sentry-sdk from 2.35.0 to 2.35.1 (#6139)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-28 10:31:03 +02:00
Stefan Agner
30db72df78 Add WebSocket proxy timeout handling (#6138)
Add TimeoutError handling for WebSocket connections to add-ons. Also
log debug information for WebSocket proxy connections.
2025-08-28 10:18:46 +02:00
Stefan Agner
00a78f372b Fix ConnectionResetError during ingress proxing (#6137)
Under certain (timing) conditions ConnectionResetError can be raised
when the client closes the connection while we are still writing to it.
Make sure to handle the appropriate exceptions to avoid flooding the
logs with stack traces.
2025-08-28 10:15:32 +02:00
dependabot[bot]
b69546f2c1 Bump orjson from 3.11.2 to 3.11.3 (#6135)
Bumps [orjson](https://github.com/ijl/orjson) from 3.11.2 to 3.11.3.
- [Release notes](https://github.com/ijl/orjson/releases)
- [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ijl/orjson/compare/3.11.2...3.11.3)

---
updated-dependencies:
- dependency-name: orjson
  dependency-version: 3.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 16:13:52 +02:00
Mike Degatano
78be155b94 Handle download retart in pull progres log (#6131) 2025-08-25 23:20:00 +02:00
Mike Degatano
9900dfc8ca Do not skip messages in pull progress log due to rounding (#6129) 2025-08-25 22:25:38 +02:00
Stefan Agner
3a1ebc9d37 Handle malformed addon map entries gracefully (#6126)
* Handle missing type attribute in add-on map config

Handle missing type attribute in the add-on `map` configuration key.

* Make sure wrong volumes are cleared in any case

Also add warning when string mapping is rejected.

* Add unit tests

* Improve test coverage
2025-08-25 22:24:46 +02:00
Jan Čermák
580c3273dc Fix guarding of timezone setting for older OS 16.2 dev builds (#6127)
As some 16.2 dev versions did not support setting of the timezone yet,
if they were not updated before the Supervisor #6099 was merged, the
system could end up unhealthy as setting of the timezone during setup
fails there. This would prevent such systems from being updated to the
new OS version.

Now that we know an exact OS version with TZ setting support, only
attempt doing it if it's supported.
2025-08-25 19:47:47 +02:00
dependabot[bot]
b889f94ca4 Bump coverage from 7.10.4 to 7.10.5 (#6125)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.4 to 7.10.5.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.4...7.10.5)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-25 14:13:13 +02:00
Stefan Agner
2d12920b35 Stop refreshing the update information on outdated OS versions (#6098)
* Stop refreshing the update information on outdated OS versions

Add `JobCondition.OS_SUPPORTED` to the updater job to avoid
refreshing update information when the OS version is unsupported.

This effectively freezes installations on unsupported OS versions
and blocks Supervisor updates. Once deployed, this ensures that any
Supervisor will always run on at least the minimum supported OS
version.

This requires to move the OS version check before Supervisor updater
initialization to allow the `JobCondition.OS_SUPPORTED` to work
correctly.

* Run only OS version check in setup loads

Instead of running a full system evaluation, only run the OS version
check right after the OS manager is loaded. This allows the
updater job condition to work correctly without running the full
system evaluation, which is not needed at this point.

* Prevent Core and Add-on updates on unsupported OS versions

Also prevent Home Assistant Core and Add-on updates on unsupported OS
versions. We could imply `JobCondition.SUPERVISOR_UPDATED` whenever
OS is outdated, but this would also prevent the OS update itself. So
we need this separate condition everywhere where
`JobCondition.SUPERVISOR_UPDATED` is used except for OS updates.

It should also be safe to let the add-on store update, we simply
don't allow the add-on to be installed or updated if the OS is
outdated.

* Remove unnecessary Host info update

It seems that the CPE information are already loaded in the HostInfo
object. Remove the unnecessary update call.

* Fix pytest

* Delay refreshing of update data

Delay refreshing of update data until after setup phase. This allows to
use the JobCondition.OS_SUPPORTED safely. We still have to fetch the
updater data in case OS information is outdated. This typically happens
on device wipe.

Note also that plug-ins will automatically refresh updater data in case
it is missing the latest version information.

This will reverse the order of updates when there are new plug-in and
Supervisor update information available (e.g. on first startup):
Previously the updater data got refreshed before the plug-in started,
which caused them to update first. Then the Supervisor got update in
startup phase. Now the updater data gets refreshed in startup phase,
which then causes the Supervisor to update first before the plug-ins
get updated after Supervisor restart.

* Fix pytest

* Fix updater tests

* Add new tests to verify that updater reload is skipped

* Fix pylint

* Apply suggestions from code review

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

* Add debug message when we delay version fetch

---------

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-08-22 11:09:56 +02:00
Stefan Agner
8a95113ebd Improve VLAN configuration (#6094)
* Fix NetworkManager connection name for VLANs

The connection name for VLANs should include the parent interface name
for better identification. This was originally the intention, but the
interface object's name property was used which appears empty at that
point.

* Disallow creating multiple connections for the same VLAN id

Only allow a single connection per interface and VLAN id. The regular
network commands can be used to alter the configuration.

* Fix pytest

* Simply connection id name generation

Always rely on the Supervisor interface representation's name attribute
to generate the NetworkManager connection id. Make sure that the name
is correctly set when creating VLAN interfaces as well.

* Special case VLAN configuration

We can't use the match information when comparing Supervisor interface
representation with D-Bus representations. Special case VLAN and
compare using VLAN ID and parent interface.

Note that this currently compares connection UUID of the parent
interface.

* Fix pytest

* Separate VLAN creation logic from apply_changes

Apply changes is really all about updating the NetworkManager settings
of a particular network interface. The base in apply_changes() is
NetworkInterface class, which is the NetworkManager Device abstraction.
All physical interfaces have such a Device hence it is always present.

The only exception is when creating a VLAN: Since it is a virtual
device, there is no device when creating a VLAN.

This separate the two cases. This makes it much easier to reason if
a VLAN already exists or not, and to handle the case where a VLAN
needs to be created.

For all other network interfaces, the apply_changes() method can
now rely on the presence of the NetworkInterface Device abstraction.

* Add VLAN test interface and VLAN exists test

Add a test which checks that an error gets raised when a VLAN for a
particular interface/id combination already exists.

* Address pylint

* Fix test_ignore_veth_only_changes pytest

* Make VLAN interface disabled to avoid test issues

* Reference setting 38 in mocked connection

* Make sure interface type matches

Require a interface type match before doing any comparision.

* Add Supervisor host network configuration tests

* Fix device type checking

* Fix pytest

* Fix tests by taking VLAN interface into account

* Fix test_load_with_network_connection_issues

This seems like a hack, but it turns out that the additional active
connection caused coresys.host.network.update() to be called, which
implicitly "fake" activated the connection. Now it seems that our
mocking causes IPv4 gateway to be set.

So in a way, the test checked a particular mock behavior instead of
actual intention.

The crucial part of this test is that we make sure the settings remain
unchanged. This is done by ensuring that the the method is still auto.

* Fix test_check_network_interface_ipv4.py

Now that we have the VLAN interface active too it will raise an issue
as well.

* Apply suggestions from code review

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

* Fix ruff check issue

---------

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-08-22 11:09:39 +02:00
dependabot[bot]
3fc1abf661 Bump types-pyyaml from 6.0.12.20250809 to 6.0.12.20250822 (#6121)
Bumps [types-pyyaml](https://github.com/typeshed-internal/stub_uploader) from 6.0.12.20250809 to 6.0.12.20250822.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-pyyaml
  dependency-version: 6.0.12.20250822
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 10:54:17 +02:00
Mike Degatano
207b665e1d Send progress updates during image pull for install/update (#6102)
* Send progress updates during image pull for install/update

* Add extra to tests about job APIs

* Sent out of date progress to sentry and combine done event

* Pulling container image layer
2025-08-22 10:41:10 +02:00
Stefan Agner
1fb15772d7 Fix docker_config check for add-ons (#6119)
* Fix docker_config check to ignore Docker VOLUME mounts

Only validate /media and /share mounts that are explicitly configured
in add-on map_volumes, not those created by Docker VOLUME statements.

* Check and test with custom map targets
2025-08-22 10:38:41 +02:00
dependabot[bot]
9740de7a83 Bump ruff from 0.12.9 to 0.12.10 (#6122)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.12.9 to 0.12.10.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.12.9...0.12.10)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.12.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 10:37:56 +02:00
dependabot[bot]
8e8d77d90c Bump types-docker from 7.1.0.20250809 to 7.1.0.20250822 (#6123)
Bumps [types-docker](https://github.com/typeshed-internal/stub_uploader) from 7.1.0.20250809 to 7.1.0.20250822.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-docker
  dependency-version: 7.1.0.20250822
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 10:37:41 +02:00
dependabot[bot]
dbce22bd08 Bump codecov/codecov-action from 5.4.3 to 5.5.0 (#6117)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.4.3 to 5.5.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5.4.3...v5.5.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 5.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-21 10:01:52 +02:00
dependabot[bot]
192d446888 Bump ciso8601 from 2.3.2 to 2.3.3 (#6118)
Bumps [ciso8601](https://github.com/closeio/ciso8601) from 2.3.2 to 2.3.3.
- [Release notes](https://github.com/closeio/ciso8601/releases)
- [Changelog](https://github.com/closeio/ciso8601/blob/master/CHANGELOG.md)
- [Commits](https://github.com/closeio/ciso8601/compare/v2.3.2...v2.3.3)

---
updated-dependencies:
- dependency-name: ciso8601
  dependency-version: 2.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-21 10:01:33 +02:00
Stefan Agner
d95ca401ec Fix git path missing or empty (#6116)
* Optimize directory_missing_or_empty function

Replace inefficient os.listdir() with os.scandir() and next() to check
if directory is empty. This avoids reading entire directory contents
into memory when we only need to know if any entry exists.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Add unit tests for directory_missing_or_empty function

Add comprehensive test coverage for the optimized directory_missing_or_empty
function, testing empty directories, directories with content, non-existent
paths, and files (non-directories).

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Apply suggestions from code review

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-08-20 17:53:30 +02:00
dependabot[bot]
07d8fd006a Bump time-machine from 2.17.0 to 2.18.0 (#6113)
* Bump time-machine from 2.17.0 to 2.18.0

Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.17.0 to 2.18.0.
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/docs/changelog.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.17.0...2.18.0)

---
updated-dependencies:
- dependency-name: time-machine
  dependency-version: 2.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Fix time_machine usage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
2025-08-20 10:37:29 +02:00
Jan Čermák
b49ce96df8 Propagate timezone setting to host in OS 16.2 and newer (#6099)
* Propagate timezone setting to host in OS 16.2 and newer

With home-assistant/operating-system#4224, timezone setting in OS can be
peristently set in HAOS as well. Propagate the timezone configured in
Supervisor config (which can be changed through general system settings
in HA Core) through the DBus API for setting the timezone.

* Persist timezone also when it's been obtained from Whoami

* Suppress pylint fixme error
2025-08-20 01:30:57 +02:00
Mike Degatano
4109c15a36 Revert .git missing check in store git (#6114) 2025-08-19 23:39:01 +02:00
dependabot[bot]
d0e2778255 Bump requests from 2.32.4 to 2.32.5 (#6112)
Bumps [requests](https://github.com/psf/requests) from 2.32.4 to 2.32.5.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.4...v2.32.5)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.32.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 22:15:16 +02:00
Copilot
014082eda8 Create repair issue when user has deprecated add-on installed (#6110)
* Initial plan

* Add deprecated addon repair issue check

Co-authored-by: agners <34061+agners@users.noreply.github.com>

* Apply ruff format

* Add remove suggestion

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

* Update tests/resolution/check/test_check_deprecated_addon.py

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: agners <34061+agners@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-08-19 22:02:36 +02:00
Petar Petrov
2324b70084 Storage space usage API (#6046)
* Storage space usage API

* Move to host API

* add tests

* fix test url

* more tests

* fix tests

* fix test

* PR comments

* update test

* tweak format and url

* add .DS_Store to .gitignore

* update tests

* test coverage

* update to new struct

* update test
2025-08-19 10:54:53 +02:00
dependabot[bot]
43f20fe24f Bump coverage from 7.10.3 to 7.10.4 (#6108)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.3 to 7.10.4.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.3...7.10.4)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 13:14:43 +02:00
TheJulianJES
8ef5eae22a Fix restrict-task-creation workflow (#6105) 2025-08-18 11:45:13 +02:00
dependabot[bot]
e5dd09ab6b Bump ruff from 0.12.8 to 0.12.9 (#6101)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 13:31:10 +02:00
dependabot[bot]
3f2db956cb Bump sentry-sdk from 2.34.1 to 2.35.0 (#6100)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-15 13:30:53 +02:00
Stefan Agner
603df92618 Sort DNS servers by NetworkManager priority (#6093)
Sort DNS servers by NetworkManager priority and make sure the order
is stable even if the priorities are equal. This avoids unnecessary
DNS plug-in restarts.
2025-08-14 15:14:32 +02:00
Mike Degatano
8a82b98e5b Improved error handling for docker image pulls (#6095)
* Improved error handling for docker image pulls

* Fix mocking in tests due to api use change
2025-08-13 18:05:27 +02:00
Jan Čermák
07dd0b7394 Bump uv to 0.8.9 (#6097) 2025-08-13 16:27:56 +02:00
dependabot[bot]
cf0a85a4b1 Bump orjson from 3.11.1 to 3.11.2 (#6096)
Bumps [orjson](https://github.com/ijl/orjson) from 3.11.1 to 3.11.2.
- [Release notes](https://github.com/ijl/orjson/releases)
- [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ijl/orjson/compare/3.11.1...3.11.2)

---
updated-dependencies:
- dependency-name: orjson
  dependency-version: 3.11.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-13 10:01:23 +02:00
dependabot[bot]
9924165cd3 Bump actions/checkout from 4 to 5.0.0 (#6092)
* Bump actions/checkout from 4 to 5

Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* Use full version number

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
2025-08-12 23:20:44 +02:00
github-actions[bot]
91392a5443 Update frontend to version 20250811.0 (#6091)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-12 09:51:46 +02:00
Copilot
fd205ce2ef Add Docker MTU configuration support for networks with non-standard MTU (#6079)
* Initial plan

* Implement Docker MTU support - core functionality

Co-authored-by: agners <34061+agners@users.noreply.github.com>

* Add comprehensive MTU tests and documentation

Co-authored-by: agners <34061+agners@users.noreply.github.com>

* Fix final linting issue in test file

Co-authored-by: agners <34061+agners@users.noreply.github.com>

* Apply suggestions from code review

* Implement reboot_required flag pattern and fix MyPy typing issue

Co-authored-by: agners <34061+agners@users.noreply.github.com>

* Update supervisor/api/docker.py

* Update supervisor/docker/manager.py

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: agners <34061+agners@users.noreply.github.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-08-12 09:19:12 +02:00
dependabot[bot]
9ec56d9266 Bump pre-commit from 4.2.0 to 4.3.0 (#6084)
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.2.0...v4.3.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-version: 4.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 14:17:05 +02:00
dependabot[bot]
886b1bd281 Bump types-docker from 7.1.0.20250705 to 7.1.0.20250809 (#6085)
Bumps [types-docker](https://github.com/typeshed-internal/stub_uploader) from 7.1.0.20250705 to 7.1.0.20250809.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-docker
  dependency-version: 7.1.0.20250809
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 14:16:50 +02:00
dependabot[bot]
ee0474edf5 Bump types-pyyaml from 6.0.12.20250516 to 6.0.12.20250809 (#6090)
Bumps [types-pyyaml](https://github.com/typeshed-internal/stub_uploader) from 6.0.12.20250516 to 6.0.12.20250809.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-pyyaml
  dependency-version: 6.0.12.20250809
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 13:58:00 +02:00
dependabot[bot]
f173489e69 Bump coverage from 7.10.2 to 7.10.3 (#6087)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.2 to 7.10.3.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.2...7.10.3)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 13:57:50 +02:00
dependabot[bot]
cee495bde3 Bump types-requests from 2.32.4.20250611 to 2.32.4.20250809 (#6089)
Bumps [types-requests](https://github.com/typeshed-internal/stub_uploader) from 2.32.4.20250611 to 2.32.4.20250809.
- [Commits](https://github.com/typeshed-internal/stub_uploader/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-version: 2.32.4.20250809
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 13:40:21 +02:00
dependabot[bot]
59104a4438 Bump pylint from 3.3.7 to 3.3.8 (#6088)
Bumps [pylint](https://github.com/pylint-dev/pylint) from 3.3.7 to 3.3.8.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v3.3.7...v3.3.8)

---
updated-dependencies:
- dependency-name: pylint
  dependency-version: 3.3.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 13:40:08 +02:00
dependabot[bot]
e4eaeb91cd Bump actions/cache from 4.2.3 to 4.2.4 (#6082)
Bumps [actions/cache](https://github.com/actions/cache) from 4.2.3 to 4.2.4.
- [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/v4.2.3...v4.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 12:23:07 +02:00
dependabot[bot]
e61d88779d Bump ruff from 0.12.7 to 0.12.8 (#6081)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.12.7 to 0.12.8.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.12.7...0.12.8)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.12.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 12:22:55 +02:00
github-actions[bot]
0513ea0438 Update frontend to version 20250806.0 (#5810)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-08-07 11:10:34 +02:00
dependabot[bot]
030927dc01 Bump debugpy from 1.8.15 to 1.8.16 (#6078)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.8.15 to 1.8.16.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.8.15...v1.8.16)

---
updated-dependencies:
- dependency-name: debugpy
  dependency-version: 1.8.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 09:40:19 +02:00
Stefan Agner
cad14bf46e Handle disks with non-existing SMART attributes (#6077)
Not all disks have all SMART attributes available, e.g. Sentry showed
devices with missing "wctemp". In practice, any SMART attribute could
be missing. Make sure we handle this gracefully.
2025-08-07 09:40:03 +02:00
Stefan Agner
5d851ad747 Ignore UTF-8 errors in addon docs (#6076)
Add-on documentation might not have valid UTF-8 encoding. Since this
is user provided content, we should not fail if it contains invalid
UTF-8 characters. Instead, we can replace them with a placeholder.
2025-08-07 09:39:53 +02:00
Stefan Agner
528032fb36 Avoid race condition on add-on installation (#6075)
The JobGroup concurrency decorator of the Addon class does not prevent
concurrent installation since the AddonManager creates a separate
object for each installation. Make sure only one add-on installation
is running at a time by using the JobGroup decorator on the
AddonManager install method instead.
2025-08-07 09:39:35 +02:00
Stefan Agner
3b093200e3 Improve JobGroup locking with external ownership tracking (#6074)
* Use context manager for Job concurrency control

* Allow to release lock outside of Job running context

* Improve JobGroup locking with external ownership tracking

Track lock ownership by job UUID instead of execution context. This
allows external lock release via job parameter.

* Fix acquire lock in nested Jobs

* Simplify nested lock tracking

* Simplify Job group lock acquisition logic

* Simplify by using helper methods

* Allow throttling with group concurrency

* Use Lock instead of Semaphore for job concurrency control

Use the same synchronization primitive (Lock) for job concurrency
control as used in job groups.

* Go back to lock ownership tracking with references

* Drop unused property `active_job_id`

* Drop unused property `can_acquire`

* Replace assert with cast
2025-08-07 00:14:58 +02:00
Stefan Agner
15ba1a3c94 Use path for actions/download-artifact to fix coverage report (#6073)
Use name and path parameters to explicitly download the coverage report
artifact. This fixes a behavioral change introduced with v5.0.0
ofactions/download-artifact.
2025-08-06 15:49:42 +02:00
Stefan Agner
8e4a87c751 Load Home Assistant OS component earlier (#6068)
Load the Home Assistant OS component earlier in the Supervisor
lifecycle to ensure that updater has board information available
when checking for updates. This makes sure that we have the latest
OS update information right on Supervisor start.
2025-08-06 10:53:30 +02:00
Mike Degatano
fdde95d849 Add an issue for disk lifetime >90% (#6069) 2025-08-06 10:40:48 +02:00
dependabot[bot]
65e5a36aa7 Bump time-machine from 2.16.0 to 2.17.0 (#6072)
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.16.0 to 2.17.0.
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/docs/changelog.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.16.0...2.17.0)

---
updated-dependencies:
- dependency-name: time-machine
  dependency-version: 2.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 10:32:47 +02:00
dependabot[bot]
bd62602cde Bump cryptography from 45.0.5 to 45.0.6 (#6071)
Bumps [cryptography](https://github.com/pyca/cryptography) from 45.0.5 to 45.0.6.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/45.0.5...45.0.6)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 45.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 10:32:33 +02:00
dependabot[bot]
f9bcc273f8 Bump actions/download-artifact from 4.3.0 to 5.0.0 (#6070)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4.3.0 to 5.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4.3.0...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 10:32:14 +02:00
Mike Degatano
059b161f4f Use actual latest version of OS in os version check (#6067) 2025-08-05 22:23:23 +02:00
dependabot[bot]
f11eb6b35a Bump docker/login-action from 3.4.0 to 3.5.0 (#6066)
Bumps [docker/login-action](https://github.com/docker/login-action) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 22:23:09 +02:00
Stefan Agner
9bee58a8b1 Migrate to JobConcurrency and JobThrottle parameters (#6065) 2025-08-05 13:24:44 +02:00
Mike Degatano
8a1e6b0895 Add unsupported reason os_version and evaluation (#6041)
* Add unsupported reason os_version and evaluation

* Order enum and add tests

* Apply suggestions from code review

* Apply suggestions from code review

---------

Co-authored-by: Stefan Agner <stefan@agner.ch>
2025-08-05 13:23:42 +02:00
Stefan Agner
f150d1b287 Return optimistic life time estimate for eMMC storage (#6063)
This avoids that we display a 10% life time use for a brand new
eMMC storage. The values are estimates anyways, and there is a
separate value which represents life time completely used (100%).
2025-08-05 10:43:57 +02:00
dependabot[bot]
628a18c6b8 Bump coverage from 7.10.1 to 7.10.2 (#6062)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.1 to 7.10.2.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.1...7.10.2)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 14:41:38 +02:00
dependabot[bot]
74e43411e5 Bump dbus-fast from 2.44.2 to 2.44.3 (#6061)
Bumps [dbus-fast](https://github.com/bluetooth-devices/dbus-fast) from 2.44.2 to 2.44.3.
- [Release notes](https://github.com/bluetooth-devices/dbus-fast/releases)
- [Changelog](https://github.com/Bluetooth-Devices/dbus-fast/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bluetooth-devices/dbus-fast/compare/v2.44.2...v2.44.3)

---
updated-dependencies:
- dependency-name: dbus-fast
  dependency-version: 2.44.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 14:41:25 +02:00
dependabot[bot]
e6b0d4144c Bump awesomeversion from 25.5.0 to 25.8.0 (#6060)
Bumps [awesomeversion](https://github.com/ludeeus/awesomeversion) from 25.5.0 to 25.8.0.
- [Release notes](https://github.com/ludeeus/awesomeversion/releases)
- [Commits](https://github.com/ludeeus/awesomeversion/compare/25.5.0...25.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 14:41:11 +02:00
Mike Degatano
033896480d Fix backup equal and add hash to objects with eq (#6059)
* Fix backup equal and add hash to objects with eq

* Add test for failed consolidate
2025-08-04 14:19:33 +02:00
dependabot[bot]
478e00c0fe Bump home-assistant/wheels from 2025.03.0 to 2025.07.0 (#6057)
Bumps [home-assistant/wheels](https://github.com/home-assistant/wheels) from 2025.03.0 to 2025.07.0.
- [Release notes](https://github.com/home-assistant/wheels/releases)
- [Commits](https://github.com/home-assistant/wheels/compare/2025.03.0...2025.07.0)

---
updated-dependencies:
- dependency-name: home-assistant/wheels
  dependency-version: 2025.07.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 14:17:42 +02:00
dependabot[bot]
6f2ba7d68c Bump mypy from 1.17.0 to 1.17.1 (#6058)
Bumps [mypy](https://github.com/python/mypy) from 1.17.0 to 1.17.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.17.0...v1.17.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-version: 1.17.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-04 14:17:13 +02:00
Mike Degatano
22afa60f55 Get lifetime info for NVMe devices (#6056)
* Get lifetime info for NVMe devices

* Fix lint and test issues

* Update tests/dbus_service_mocks/udisks2_manager.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Stefan Agner <stefan@agner.ch>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-04 13:53:56 +02:00
Stefan Agner
9f2fda5dc7 Update to Alpine 3.22 (#6054)
Update the Supervisor base image to Alpine 3.22 for all architectures.
2025-08-04 09:50:27 +02:00
Stefan Agner
27b092aed0 Block OS updates when the system is unhealthy (#6053)
* Block OS updates when the system is unhealthy

In #6024 we mark a system as unhealthy when multiple OS installations
were found. The idea was to block OS updates in this case. However, it
turns out that the OS update job was not checking the system health
and thus allowed updates even when the system was marked as unhealthy.

This commit adds the `JobCondition.HEALTHY` condition to the OS update
job, ensuring that OS updates are only performed when the system is
healthy.

Users can force an OS update still by using
`ha jobs options --ignore-conditions healthy`.

* Add test for update of unhealthy system

---------

Co-authored-by: Jan Čermák <sairon@sairon.cz>
2025-07-31 11:23:57 +02:00
dependabot[bot]
3af13cb7e2 Bump sentry-sdk from 2.34.0 to 2.34.1 (#6052)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.34.0 to 2.34.1.
- [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/2.34.0...2.34.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 10:43:49 +02:00
Stefan Agner
6871ea4b81 Split execution limit in concurrency and throttle parameters (#6013)
* Split execution limit in concurrency and throttle parameters

Currently the execution limit combines two ortogonal features: Limit
concurrency and throttle execution. This change separates the two
features, allowing for more flexible configuration of job execution.

Ultimately I want to get rid of the old limit parameter. But for ease
of review and migration, I'd like to do this in two steps: First
introduce the new parameters, and map the old limit parameters to the
new parameters. Then, in a second step, remove the old limit parameter
and migrate all users to the new concurrency and throttle parameters
as needed.

* Introduce common lock release method

* Fix THROTTLE_WAIT behavior

The concurrency QUEUE does not really QUEUE throttle limits.

* Add documentation for new concurrency/throttle Job options

* Handle group options for concurrency and throttle separately

* Fix GROUP_THROTTLE_WAIT concurrency setting

We need to use the QUEUE concurrency setting instead of GROUP_QUEUE
for the GROUP_THROTTLE_WAIT execution limit. Otherwise the
test_jobs_decorator.py::test_execution_limit_group_throttle_wait
test deadlocks.

The reason this deadlocks is because GROUP_QUEUE concurrency doesn't
really work because we only can release a group lock if the job is
actually running.

Or put differently, throttling isn't supported with GROUP_*
concurrency options.

* Prevent using any throttling with group concurrency

The group concurrency modes (reject and queue) are not compatible with
any throttling, since we currently can't unlock the group lock when
a job doesn't get started (which is the case when throttling is
applied).

* Fix commit in group rate limit

* Explain the deadlock issue with group locks in code

* Handle locking correctly on throttle limit exceptions

* Introduce pytest for new job decorator combinations
2025-07-30 22:12:14 +02:00
dependabot[bot]
cf77ab2290 Bump aiohttp from 3.12.14 to 3.12.15 (#6049)
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-version: 3.12.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 14:34:14 +02:00
dependabot[bot]
ceeffa3284 Bump ruff from 0.12.5 to 0.12.7 (#6051)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.12.5 to 0.12.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.12.5...0.12.7)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.12.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 14:33:07 +02:00
dependabot[bot]
31f2f70cd9 Bump sentry-sdk from 2.33.2 to 2.34.0 (#6050)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.33.2 to 2.34.0.
- [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/2.33.2...2.34.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 14:32:11 +02:00
Stefan Agner
deac85bddb Scrub WiFi fields from Sentry events (#6048)
Make sure WiFi fields are scrubbed from Sentry events to prevent
accidental exposure of sensitive information.
2025-07-29 17:42:43 +02:00
Stefan Agner
7dcf5ba631 Enable IPv6 for containers on new installations (#6029)
* Enable IPv6 by default for new installations

Enable IPv6 by default for new Supervisor installations. Let's also
make the `enable_ipv6` attribute nullable, so we can distinguish
between "not set" and "set to false".

* Add pytest

* Add log message that system restart is required for IPv6 changes

* Fix API pytest

* Create resolution center issue when reboot is required

* Order log after actual setter call
2025-07-29 15:59:03 +02:00
dependabot[bot]
a004830131 Bump orjson from 3.11.0 to 3.11.1 (#6045)
Bumps [orjson](https://github.com/ijl/orjson) from 3.11.0 to 3.11.1.
- [Release notes](https://github.com/ijl/orjson/releases)
- [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ijl/orjson/compare/3.11.0...3.11.1)

---
updated-dependencies:
- dependency-name: orjson
  dependency-version: 3.11.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 10:41:42 +02:00
dependabot[bot]
a8cc6c416d Bump coverage from 7.10.0 to 7.10.1 (#6044)
Bumps [coverage](https://github.com/nedbat/coveragepy) from 7.10.0 to 7.10.1.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/7.10.0...7.10.1)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 10:41:19 +02:00
dependabot[bot]
74b26642b0 Bump ruff from 0.12.4 to 0.12.5 (#6042)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-27 20:20:27 +02:00
dependabot[bot]
5e26ab5f4a Bump gitpython from 3.1.44 to 3.1.45 (#6039)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-27 20:14:24 +02:00
dependabot[bot]
a841cb8282 Bump coverage from 7.9.2 to 7.10.0 (#6043) 2025-07-27 10:31:48 +02:00
dependabot[bot]
3b1b03c8a7 Bump dbus-fast from 2.44.1 to 2.44.2 (#6038)
Bumps [dbus-fast](https://github.com/bluetooth-devices/dbus-fast) from 2.44.1 to 2.44.2.
- [Release notes](https://github.com/bluetooth-devices/dbus-fast/releases)
- [Changelog](https://github.com/Bluetooth-Devices/dbus-fast/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bluetooth-devices/dbus-fast/compare/v2.44.1...v2.44.2)

---
updated-dependencies:
- dependency-name: dbus-fast
  dependency-version: 2.44.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 16:06:19 -04:00
dependabot[bot]
680428f304 Bump sentry-sdk from 2.33.0 to 2.33.2 (#6037)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.33.0 to 2.33.2.
- [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/2.33.0...2.33.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 12:44:35 -04:00
dependabot[bot]
f34128c37e Bump ruff from 0.12.3 to 0.12.4 (#6031)
---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.12.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 12:43:56 -04:00
dependabot[bot]
2ed0682b34 Bump sigstore/cosign-installer from 3.9.1 to 3.9.2 (#6032) 2025-07-18 10:00:58 +02:00
Stefan Agner
fbb0915ef8 Mark system as unhealthy if multiple OS installations are found (#6024)
* Add resolution check for duplicate OS installations

* Only create single issue/use separate unhealthy type

* Check MBR partition UUIDs as well

* Use partlabel

* Use generator to avoid code duplication

* Add list of devices, avoid unnecessary exception handling

* Run check only on HAOS

* Fix message formatting

* Fix and simplify pytests

* Fix UnhealthyReason sort order
2025-07-17 10:06:35 +02:00
Stefan Agner
780ae1e15c Check for duplicate data disks only when the OS is available (#6025)
* Check for duplicate data disks only when the OS is available

Supervised installations do not have a specific data disk, so only
check for duplicate data disks on Home Assistant OS.

* Enable OS for multiple data disks check test
2025-07-16 10:43:15 +02:00
dependabot[bot]
c617358855 Bump orjson from 3.10.18 to 3.11.0 (#6028)
Bumps [orjson](https://github.com/ijl/orjson) from 3.10.18 to 3.11.0.
- [Release notes](https://github.com/ijl/orjson/releases)
- [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ijl/orjson/compare/3.10.18...3.11.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 09:24:34 +02:00
dependabot[bot]
b679c4f4d8 Bump sentry-sdk from 2.32.0 to 2.33.0 (#6027)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.32.0 to 2.33.0.
- [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/2.32.0...2.33.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 09:20:28 +02:00
dependabot[bot]
c946c421f2 Bump debugpy from 1.8.14 to 1.8.15 (#6026)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.8.14 to 1.8.15.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.8.14...v1.8.15)

---
updated-dependencies:
- dependency-name: debugpy
  dependency-version: 1.8.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 09:19:44 +02:00
dependabot[bot]
aeabf7ea25 Bump blockbuster from 1.5.24 to 1.5.25 (#6020)
Bumps [blockbuster](https://github.com/cbornet/blockbuster) from 1.5.24 to 1.5.25.
- [Release notes](https://github.com/cbornet/blockbuster/releases)
- [Commits](https://github.com/cbornet/blockbuster/commits/v1.5.25)

---
updated-dependencies:
- dependency-name: blockbuster
  dependency-version: 1.5.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 09:18:57 +02:00
dependabot[bot]
365b838abf Bump mypy from 1.16.1 to 1.17.0 (#6019)
Bumps [mypy](https://github.com/python/mypy) from 1.16.1 to 1.17.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.16.1...v1.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 09:08:57 +02:00
Stefan Agner
99c040520e Drop ensure_builtin_repositories() (#6012)
* Drop ensure_builtin_repositories

With the new Repository classes we have the is_builtin property, so we
can easily make sure that built-ins are not removed. This allows us to
further cleanup the code by removing the ensure_builtin_repositories
function and the ALL_BUILTIN_REPOSITORIES constant.

* Make sure we add built-ins on load

* Reuse default set and avoid unnecessary copy

Reuse default set and avoid unnecessary copying during validation if
the default is not being used.
2025-07-14 22:19:06 +02:00
dependabot[bot]
eefe2f2e06 Bump aiohttp from 3.12.13 to 3.12.14 (#6014)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-14 11:43:55 +02:00
dependabot[bot]
a366e36b37 Bump ruff from 0.12.2 to 0.12.3 (#6016)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-14 11:19:08 +02:00
dependabot[bot]
27a2fde9e1 Bump astroid from 3.3.10 to 3.3.11 (#6017)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-14 11:18:54 +02:00
Stefan Agner
9a0f530a2f Add Supervisor connectivity check after DNS restart (#6005)
* Add Supervisor connectivity check after DNS restart

When the DNS plug-in got restarted, check Supervisor connectivity
in case the DNS plug-in configuration change influenced Supervisor
connectivity. This is helpful when a DHCP server gets started after
Home Assistant is up. In that case the network provided DNS server
(local DNS server) becomes available after the DNS plug-in restart.

Without this change, the Supervisor connectivity will remain false
until the a Job triggers a connectivity check, for example the
periodic update check (which causes a updater and store reload) by
Core.

* Fix pytest and add coverage for new functionality
2025-07-10 11:08:10 +02:00
Stefan Agner
baf9695cf7 Refactoring around add-on store Repository classes (#5990)
* Rename repository fixture to test_repository

Also don't remove the built-in repositories. The list was incomplete,
and tests don't seem to require that anymore.

* Get rid of StoreType

The type doesn't have much value, we have constant strings anyways.

* Introduce types.py

* Use slug to determine which repository urls to return

* Simplify BuiltinRepository enum

* Mock GitRepo load

* Improve URL handling and repository creation logic

* Refactor update_repositories

* Get rid of get_from_url

It is no longer used in production code.

* More refactoring

* Address pylint

* Introduce is_git_based property to Repository class

Return all git based URLs, including the Core repository.

* Revert "Introduce is_git_based property to Repository class"

This reverts commit dfd5ad79bf.

* Fold type.py into const.py

Align more with how Supervisor code is typically structured.

* Update supervisor/store/__init__.py

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

* Apply repository remove suggestion

* Fix tests

---------

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-07-10 11:07:53 +02:00
Stefan Agner
7873c457d5 Small improvement to Copilot instructions (#6011) 2025-07-10 11:05:59 +02:00
Stefan Agner
cbc48c381f Return 401 Unauthorized when using json/url encoded auth fails (#5844)
When authentication using JSON payload or URL encoded payload fails,
use the generic HTTP response code 401 Unauthorized instead of 400
Bad Request.

This is a more appropriate response code for authentication errors
and is consistent with the behavior of other authentication methods.
2025-07-10 08:38:00 +02:00
Franck Nijhof
11e37011bd Add Task issue form (#6007) 2025-07-09 16:58:10 +02:00
Franck Nijhof
cfda559a90 Adjust feature request links in issue reporting (#6009) 2025-07-09 16:44:35 +02:00
Mike Degatano
806bd9f52c Apply store reload suggestion automatically on connectivity change (#6004)
* Apply store reload suggestion automatically on connectivity change

* Use sys_bus not coresys.bus

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-09 16:43:51 +02:00
Stefan Agner
953f7d01d7 Improve DNS plug-in restart (#5999)
* Improve DNS plug-in restart

Instead of simply go by PrimaryConnectioon change, use the DnsManager
Configuration property. This property is ultimately used to write the
DNS plug-in configuration, so it is really the relevant information
we pass on to the plug-in.

* Check for changes and restart DNS plugin

* Check for changes in plug-in DNS

Cache last local (NetworkManager) provided DNS servers. Check against
this DNS server list when deciding when to restart the DNS plug-in.

* Check connectivity unthrottled in certain situations

* Fix pytest

* Fix pytest

* Improve test coverage for DNS plugins restart functionality

* Apply suggestions from code review

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>

* Debounce local DNS changes and event based connectivity checks

* Remove connection check logic

* Remove unthrottled connectivity check

* Fix delayed call

* Store restart task and cancel in case a restart is running

* Improve DNS configuration change tests

* Remove stale code

* Improve DNS plug-in tests, less mocking

* Cover multiple private functions at once

Improve tests around notify_locals_changed() to cover multiple
functions at once.

---------

Co-authored-by: Mike Degatano <michael.degatano@gmail.com>
2025-07-09 11:35:03 +02:00
Felipe Santos
381e719a0e Allow to force rebuild of add-ons (#6002) 2025-07-07 21:41:18 +02:00
Ruben van Dijk
296071067d Fix multiple set-cookie headers with addons ingress (#5996) 2025-07-07 19:27:39 +02:00
4240 changed files with 14077 additions and 5765 deletions

View File

@@ -8,7 +8,7 @@ body:
If you have a feature or enhancement request, please use the [feature request][fr] section of our [Community Forum][fr].
[fr]: https://community.home-assistant.io/c/feature-requests
[fr]: https://github.com/orgs/home-assistant/discussions
- type: textarea
validations:
required: true
@@ -75,7 +75,7 @@ body:
description: >
The System information can be found in [Settings -> System -> Repairs -> (three dot menu) -> System Information](https://my.home-assistant.io/redirect/system_health/).
Click the copy button at the bottom of the pop-up and paste it here.
[![Open your Home Assistant instance and show health information about your system.](https://my.home-assistant.io/badges/system_health.svg)](https://my.home-assistant.io/redirect/system_health/)
- type: textarea
attributes:
@@ -85,7 +85,7 @@ body:
Supervisor diagnostics can be found in [Settings -> Devices & services](https://my.home-assistant.io/redirect/integrations/).
Find the card that says `Home Assistant Supervisor`, open it, and select the three dot menu of the Supervisor integration entry
and select 'Download diagnostics'.
**Please drag-and-drop the downloaded file into the textbox below. Do not copy and paste its contents.**
- type: textarea
attributes:

53
.github/ISSUE_TEMPLATE/task.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Task
description: For staff only - Create a task
type: Task
body:
- type: markdown
attributes:
value: |
## ⚠️ RESTRICTED ACCESS
**This form is restricted to Open Home Foundation staff and authorized contributors only.**
If you are a community member wanting to contribute, please:
- For bug reports: Use the [bug report form](https://github.com/home-assistant/supervisor/issues/new?template=bug_report.yml)
- For feature requests: Submit to [Feature Requests](https://github.com/orgs/home-assistant/discussions)
---
### For authorized contributors
Use this form to create tasks for development work, improvements, or other actionable items that need to be tracked.
- type: textarea
id: description
attributes:
label: Description
description: |
Provide a clear and detailed description of the task that needs to be accomplished.
Be specific about what needs to be done, why it's important, and any constraints or requirements.
placeholder: |
Describe the task, including:
- What needs to be done
- Why this task is needed
- Expected outcome
- Any constraints or requirements
validations:
required: true
- type: textarea
id: additional_context
attributes:
label: Additional context
description: |
Any additional information, links, research, or context that would be helpful.
Include links to related issues, research, prototypes, roadmap opportunities etc.
placeholder: |
- Roadmap opportunity: [link]
- Epic: [link]
- Feature request: [link]
- Technical design documents: [link]
- Prototype/mockup: [link]
- Dependencies: [links]
validations:
required: false

View File

@@ -251,8 +251,8 @@ async def backup_full(self, request: web.Request) -> dict[str, Any]:
### Development Commands
```bash
# Run tests with coverage
pytest tests/ --cov=supervisor --cov-report=term-missing
# Run tests, adjust paths as necessary
pytest -qsx tests/
# Linting and formatting
ruff check supervisor/
@@ -275,6 +275,7 @@ Always run the pre-commit hooks at the end of code editing.
- Use `self.sys_run_in_executor()` for blocking operations
- Access Docker via `self.sys_docker` not direct Docker API
- Use constants from `const.py` instead of hardcoding
- Store types in (per-module) `const.py` (e.g. supervisor/store/const.py)
**❌ Avoid These Patterns**:
- Direct Docker API usage - use Supervisor's Docker manager

View File

@@ -53,7 +53,7 @@ jobs:
requirements: ${{ steps.requirements.outputs.changed }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
@@ -70,7 +70,7 @@ jobs:
- name: Get changed files
id: changed_files
if: steps.version.outputs.publish == 'false'
uses: masesgroup/retrieve-changed-files@v3.0.0
uses: masesgroup/retrieve-changed-files@491e80760c0e28d36ca6240a27b1ccb8e1402c13 # v3.0.0
- name: Check if requirements files changed
id: requirements
@@ -92,7 +92,7 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
@@ -104,9 +104,10 @@ jobs:
echo "CARGO_NET_GIT_FETCH_WITH_CLI=true"
) > .env_file
# home-assistant/wheels doesn't support sha pinning
- name: Build wheels
if: needs.init.outputs.requirements == 'true'
uses: home-assistant/wheels@2025.03.0
uses: home-assistant/wheels@2025.09.0
with:
abi: cp313
tag: musllinux_1_2
@@ -125,15 +126,15 @@ jobs:
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
if: needs.init.outputs.publish == 'true'
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Install Cosign
if: needs.init.outputs.publish == 'true'
uses: sigstore/cosign-installer@v3.9.1
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
with:
cosign-release: "v2.4.3"
cosign-release: "v2.5.3"
- name: Install dirhash and calc hash
if: needs.init.outputs.publish == 'true'
@@ -149,7 +150,7 @@ jobs:
- name: Login to GitHub Container Registry
if: needs.init.outputs.publish == 'true'
uses: docker/login-action@v3.4.0
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -159,8 +160,9 @@ jobs:
if: needs.init.outputs.publish == 'false'
run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV
# home-assistant/builder doesn't support sha pinning
- name: Build supervisor
uses: home-assistant/builder@2025.03.0
uses: home-assistant/builder@2025.09.0
with:
args: |
$BUILD_ARGS \
@@ -178,7 +180,7 @@ jobs:
steps:
- name: Checkout the repository
if: needs.init.outputs.publish == 'true'
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Initialize git
if: needs.init.outputs.publish == 'true'
@@ -203,11 +205,12 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# home-assistant/builder doesn't support sha pinning
- name: Build the Supervisor
if: needs.init.outputs.publish != 'true'
uses: home-assistant/builder@2025.03.0
uses: home-assistant/builder@2025.09.0
with:
args: |
--test \

View File

@@ -26,15 +26,15 @@ jobs:
name: Prepare Python dependencies
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python
id: python
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -48,7 +48,7 @@ jobs:
pip install -r requirements.txt -r requirements_tests.txt
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
lookup-only: true
@@ -68,15 +68,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -88,7 +88,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: |
@@ -111,15 +111,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -131,7 +131,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: |
@@ -154,7 +154,7 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Register hadolint problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
@@ -169,15 +169,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -189,7 +189,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: |
@@ -213,15 +213,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -233,7 +233,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: |
@@ -257,15 +257,15 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -293,9 +293,9 @@ jobs:
needs: prepare
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
@@ -307,7 +307,7 @@ jobs:
echo "key=mypy-${{ env.MYPY_CACHE_VERSION }}-$mypy_version-$(date -u '+%Y-%m-%dT%H:%M:%s')" >> $GITHUB_OUTPUT
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: >-
@@ -318,7 +318,7 @@ jobs:
echo "Failed to restore Python virtual environment from cache"
exit 1
- name: Restore mypy cache
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: .mypy_cache
key: >-
@@ -339,19 +339,19 @@ jobs:
name: Run tests Python ${{ needs.prepare.outputs.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Install Cosign
uses: sigstore/cosign-installer@v3.9.1
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
with:
cosign-release: "v2.4.3"
cosign-release: "v2.5.3"
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -386,9 +386,9 @@ jobs:
-o console_output_style=count \
tests
- name: Upload coverage artifact
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: coverage-${{ matrix.python-version }}
name: coverage
path: .coverage
include-hidden-files: true
@@ -398,15 +398,15 @@ jobs:
needs: ["pytest", "prepare"]
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up Python ${{ needs.prepare.outputs.python-version }}
uses: actions/setup-python@v5.6.0
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
id: python
with:
python-version: ${{ needs.prepare.outputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@v4.2.3
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: venv
key: |
@@ -417,7 +417,10 @@ jobs:
echo "Failed to restore Python virtual environment from cache"
exit 1
- name: Download all coverage artifacts
uses: actions/download-artifact@v4.3.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: coverage
path: coverage/
- name: Combine coverage results
run: |
. venv/bin/activate
@@ -425,4 +428,4 @@ jobs:
coverage report
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5.4.3
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1

View File

@@ -9,7 +9,7 @@ jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5.0.1
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
with:
github-token: ${{ github.token }}
issue-inactive-days: "30"

View File

@@ -11,7 +11,7 @@ jobs:
name: Release Drafter
steps:
- name: Checkout the repository
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
@@ -36,7 +36,7 @@ jobs:
echo "version=$datepre.$newpost" >> "$GITHUB_OUTPUT"
- name: Run Release Drafter
uses: release-drafter/release-drafter@v6.1.0
uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0
with:
tag: ${{ steps.version.outputs.version }}
name: ${{ steps.version.outputs.version }}

View File

@@ -0,0 +1,58 @@
name: Restrict task creation
# yamllint disable-line rule:truthy
on:
issues:
types: [opened]
jobs:
check-authorization:
runs-on: ubuntu-latest
# Only run if this is a Task issue type (from the issue form)
if: github.event.issue.type.name == 'Task'
steps:
- name: Check if user is authorized
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const issueAuthor = context.payload.issue.user.login;
// Check if user is an organization member
try {
await github.rest.orgs.checkMembershipForUser({
org: 'home-assistant',
username: issueAuthor
});
console.log(`✅ ${issueAuthor} is an organization member`);
return; // Authorized
} catch (error) {
console.log(`❌ ${issueAuthor} is not authorized to create Task issues`);
}
// Close the issue with a comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `Hi @${issueAuthor}, thank you for your contribution!\n\n` +
`Task issues are restricted to Open Home Foundation staff and authorized contributors.\n\n` +
`If you would like to:\n` +
`- Report a bug: Please use the [bug report form](https://github.com/home-assistant/supervisor/issues/new?template=bug_report.yml)\n` +
`- Request a feature: Please submit to [Feature Requests](https://github.com/orgs/home-assistant/discussions)\n\n` +
`If you believe you should have access to create Task issues, please contact the maintainers.`
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
state: 'closed'
});
// Add a label to indicate this was auto-closed
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['auto-closed']
});

View File

@@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Sentry Release
uses: getsentry/action-release@v3.2.0
uses: getsentry/action-release@4f502acc1df792390abe36f2dcb03612ef144818 # v3.3.0
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}

View File

@@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9.1.0
- uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30

View File

@@ -14,10 +14,10 @@ jobs:
latest_version: ${{ steps.latest_frontend_version.outputs.latest_tag }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get latest frontend release
id: latest_frontend_version
uses: abatilo/release-info-action@v1.3.3
uses: abatilo/release-info-action@32cb932219f1cee3fc4f4a298fd65ead5d35b661 # v1.3.3
with:
owner: home-assistant
repo: frontend
@@ -49,7 +49,7 @@ jobs:
if: needs.check-version.outputs.skip != 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Clear www folder
run: |
rm -rf supervisor/api/panel/*
@@ -57,7 +57,7 @@ jobs:
run: |
echo "${{ needs.check-version.outputs.latest_version }}" > .ha-frontend-version
- name: Download release assets
uses: robinraju/release-downloader@v1
uses: robinraju/release-downloader@daf26c55d821e836577a15f77d86ddc078948b05 # v1.12
with:
repository: 'home-assistant/frontend'
tag: ${{ needs.check-version.outputs.latest_version }}
@@ -68,7 +68,7 @@ jobs:
run: |
rm -f supervisor/api/panel/home_assistant_frontend_supervisor-*.tar.gz
- name: Create PR
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
with:
commit-message: "Update frontend to version ${{ needs.check-version.outputs.latest_version }}"
branch: autoupdate-frontend

3
.gitignore vendored
View File

@@ -100,3 +100,6 @@ ENV/
# mypy
/.mypy_cache/*
/.dmypy.json
# Mac
.DS_Store

View File

@@ -1 +1 @@
20250401.0
20250811.0

View File

@@ -29,7 +29,7 @@ RUN \
\
&& curl -Lso /usr/bin/cosign "https://github.com/home-assistant/cosign/releases/download/${COSIGN_VERSION}/cosign_${BUILD_ARCH}" \
&& chmod a+x /usr/bin/cosign \
&& pip3 install uv==0.6.17
&& pip3 install uv==0.8.9
# Install requirements
COPY requirements.txt .

View File

@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-hassio-supervisor
build_from:
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.13-alpine3.21
armhf: ghcr.io/home-assistant/armhf-base-python:3.13-alpine3.21
armv7: ghcr.io/home-assistant/armv7-base-python:3.13-alpine3.21
amd64: ghcr.io/home-assistant/amd64-base-python:3.13-alpine3.21
i386: ghcr.io/home-assistant/i386-base-python:3.13-alpine3.21
aarch64: ghcr.io/home-assistant/aarch64-base-python:3.13-alpine3.22
armhf: ghcr.io/home-assistant/armhf-base-python:3.13-alpine3.22
armv7: ghcr.io/home-assistant/armv7-base-python:3.13-alpine3.22
amd64: ghcr.io/home-assistant/amd64-base-python:3.13-alpine3.22
i386: ghcr.io/home-assistant/i386-base-python:3.13-alpine3.22
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io
@@ -12,7 +12,7 @@ cosign:
base_identity: https://github.com/home-assistant/docker-base/.*
identity: https://github.com/home-assistant/supervisor/.*
args:
COSIGN_VERSION: 2.4.3
COSIGN_VERSION: 2.5.3
labels:
io.hass.type: supervisor
org.opencontainers.image.title: Home Assistant Supervisor

View File

@@ -1,30 +1,30 @@
aiodns==3.5.0
aiohttp==3.12.13
aiohttp==3.12.15
atomicwrites-homeassistant==1.4.1
attrs==25.3.0
awesomeversion==25.5.0
blockbuster==1.5.24
awesomeversion==25.8.0
blockbuster==1.5.25
brotli==1.1.0
ciso8601==2.3.2
ciso8601==2.3.3
colorlog==6.9.0
cpe==1.3.1
cryptography==45.0.5
debugpy==1.8.14
cryptography==46.0.1
debugpy==1.8.17
deepmerge==2.0
dirhash==0.5.0
docker==7.1.0
faust-cchardet==2.1.19
gitpython==3.1.44
gitpython==3.1.45
jinja2==3.1.6
log-rate-limit==1.4.2
orjson==3.10.18
orjson==3.11.3
pulsectl==24.12.0
pyudev==0.24.3
PyYAML==6.0.2
requests==2.32.4
requests==2.32.5
securetar==2025.2.1
sentry-sdk==2.32.0
sentry-sdk==2.38.0
setuptools==80.9.0
voluptuous==0.15.2
dbus-fast==2.44.1
dbus-fast==2.44.3
zlib-fast==0.2.1

View File

@@ -1,16 +1,16 @@
astroid==3.3.10
coverage==7.9.2
mypy==1.16.1
pre-commit==4.2.0
pylint==3.3.7
astroid==3.3.11
coverage==7.10.7
mypy==1.18.2
pre-commit==4.3.0
pylint==3.3.8
pytest-aiohttp==1.1.0
pytest-asyncio==0.25.2
pytest-cov==6.2.1
pytest-cov==7.0.0
pytest-timeout==2.4.0
pytest==8.4.1
ruff==0.12.2
time-machine==2.16.0
types-docker==7.1.0.20250705
types-pyyaml==6.0.12.20250516
types-requests==2.32.4.20250611
pytest==8.4.2
ruff==0.13.1
time-machine==2.19.0
types-docker==7.1.0.20250916
types-pyyaml==6.0.12.20250915
types-requests==2.32.4.20250913
urllib3==2.5.0

View File

@@ -66,10 +66,23 @@ if __name__ == "__main__":
_LOGGER.info("Setting up Supervisor")
loop.run_until_complete(coresys.core.setup())
bootstrap.register_signal_handlers(loop, coresys)
# Create startup task that can be cancelled gracefully
startup_task = loop.create_task(coresys.core.start())
def shutdown_handler() -> None:
"""Handle shutdown signals gracefully during startup."""
if not startup_task.done():
_LOGGER.warning("Supervisor startup interrupted by shutdown signal")
startup_task.cancel()
coresys.create_task(coresys.core.stop())
bootstrap.register_signal_handlers(loop, shutdown_handler)
try:
loop.run_until_complete(coresys.core.start())
loop.run_until_complete(startup_task)
except asyncio.CancelledError:
_LOGGER.warning("Supervisor startup cancelled")
except Exception as err: # pylint: disable=broad-except
# Supervisor itself is running at this point, just something didn't
# start as expected. Log with traceback to get more insights for

View File

@@ -67,17 +67,16 @@ from ..docker.monitor import DockerContainerStateEvent
from ..docker.stats import DockerStats
from ..exceptions import (
AddonConfigurationError,
AddonNotSupportedError,
AddonsError,
AddonsJobError,
AddonsNotSupportedError,
ConfigurationFileError,
DockerError,
HomeAssistantAPIError,
HostAppArmorError,
)
from ..hardware.data import Device
from ..homeassistant.const import WSEvent
from ..jobs.const import JobExecutionLimit
from ..jobs.const import JobConcurrency, JobThrottle
from ..jobs.decorator import Job
from ..resolution.const import ContextType, IssueType, UnhealthyReason
from ..resolution.data import Issue
@@ -733,8 +732,8 @@ class Addon(AddonModel):
@Job(
name="addon_unload",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def unload(self) -> None:
"""Unload add-on and remove data."""
@@ -766,8 +765,8 @@ class Addon(AddonModel):
@Job(
name="addon_install",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def install(self) -> None:
"""Install and setup this addon."""
@@ -807,8 +806,8 @@ class Addon(AddonModel):
@Job(
name="addon_uninstall",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def uninstall(
self, *, remove_config: bool, remove_image: bool = True
@@ -842,8 +841,7 @@ class Addon(AddonModel):
# Cleanup Ingress panel from sidebar
if self.ingress_panel:
self.ingress_panel = False
with suppress(HomeAssistantAPIError):
await self.sys_ingress.update_hass_panel(self)
await self.sys_ingress.update_hass_panel(self)
# Cleanup Ingress dynamic port assignment
need_ingress_token_cleanup = False
@@ -873,8 +871,8 @@ class Addon(AddonModel):
@Job(
name="addon_update",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def update(self) -> asyncio.Task | None:
"""Update this addon to latest version.
@@ -923,8 +921,8 @@ class Addon(AddonModel):
@Job(
name="addon_rebuild",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def rebuild(self) -> asyncio.Task | None:
"""Rebuild this addons container and image.
@@ -1068,8 +1066,8 @@ class Addon(AddonModel):
@Job(
name="addon_start",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def start(self) -> asyncio.Task:
"""Set options and start add-on.
@@ -1117,8 +1115,8 @@ class Addon(AddonModel):
@Job(
name="addon_stop",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def stop(self) -> None:
"""Stop add-on."""
@@ -1131,8 +1129,8 @@ class Addon(AddonModel):
@Job(
name="addon_restart",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def restart(self) -> asyncio.Task:
"""Restart add-on.
@@ -1166,13 +1164,13 @@ class Addon(AddonModel):
@Job(
name="addon_write_stdin",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def write_stdin(self, data) -> None:
"""Write data to add-on stdin."""
if not self.with_stdin:
raise AddonsNotSupportedError(
raise AddonNotSupportedError(
f"Add-on {self.slug} does not support writing to stdin!", _LOGGER.error
)
@@ -1200,8 +1198,8 @@ class Addon(AddonModel):
@Job(
name="addon_begin_backup",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def begin_backup(self) -> bool:
"""Execute pre commands or stop addon if necessary.
@@ -1222,8 +1220,8 @@ class Addon(AddonModel):
@Job(
name="addon_end_backup",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def end_backup(self) -> asyncio.Task | None:
"""Execute post commands or restart addon if necessary.
@@ -1260,8 +1258,8 @@ class Addon(AddonModel):
@Job(
name="addon_backup",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def backup(self, tar_file: tarfile.TarFile) -> asyncio.Task | None:
"""Backup state of an add-on.
@@ -1368,8 +1366,8 @@ class Addon(AddonModel):
@Job(
name="addon_restore",
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
concurrency=JobConcurrency.GROUP_REJECT,
)
async def restore(self, tar_file: tarfile.TarFile) -> asyncio.Task | None:
"""Restore state of an add-on.
@@ -1419,7 +1417,7 @@ class Addon(AddonModel):
# If available
if not self._available(data[ATTR_SYSTEM]):
raise AddonsNotSupportedError(
raise AddonNotSupportedError(
f"Add-on {self.slug} is not available for this platform",
_LOGGER.error,
)
@@ -1521,10 +1519,10 @@ class Addon(AddonModel):
@Job(
name="addon_restart_after_problem",
limit=JobExecutionLimit.GROUP_THROTTLE_RATE_LIMIT,
throttle_period=WATCHDOG_THROTTLE_PERIOD,
throttle_max_calls=WATCHDOG_THROTTLE_MAX_CALLS,
on_condition=AddonsJobError,
throttle=JobThrottle.GROUP_RATE_LIMIT,
)
async def _restart_after_problem(self, state: ContainerState):
"""Restart unhealthy or failed addon."""

View File

@@ -9,16 +9,17 @@ from typing import Self, Union
from attr import evolve
from supervisor.jobs.const import JobConcurrency
from ..const import AddonBoot, AddonStartup, AddonState
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import (
AddonNotSupportedError,
AddonsError,
AddonsJobError,
AddonsNotSupportedError,
CoreDNSError,
DockerError,
HassioError,
HomeAssistantAPIError,
)
from ..jobs.decorator import Job, JobCondition
from ..resolution.const import ContextType, IssueType, SuggestionType
@@ -180,8 +181,11 @@ class AddonManager(CoreSysAttributes):
name="addon_manager_install",
conditions=ADDON_UPDATE_CONDITIONS,
on_condition=AddonsJobError,
concurrency=JobConcurrency.QUEUE,
)
async def install(self, slug: str) -> None:
async def install(
self, slug: str, *, validation_complete: asyncio.Event | None = None
) -> None:
"""Install an add-on."""
self.sys_jobs.current.reference = slug
@@ -194,6 +198,10 @@ class AddonManager(CoreSysAttributes):
store.validate_availability()
# If being run in the background, notify caller that validation has completed
if validation_complete:
validation_complete.set()
await Addon(self.coresys, slug).install()
_LOGGER.info("Add-on '%s' successfully installed", slug)
@@ -223,7 +231,11 @@ class AddonManager(CoreSysAttributes):
on_condition=AddonsJobError,
)
async def update(
self, slug: str, backup: bool | None = False
self,
slug: str,
backup: bool | None = False,
*,
validation_complete: asyncio.Event | None = None,
) -> asyncio.Task | None:
"""Update add-on.
@@ -248,6 +260,10 @@ class AddonManager(CoreSysAttributes):
# Check if available, Maybe something have changed
store.validate_availability()
# If being run in the background, notify caller that validation has completed
if validation_complete:
validation_complete.set()
if backup:
await self.sys_backups.do_backup_partial(
name=f"addon_{addon.slug}_{addon.version}",
@@ -266,7 +282,7 @@ class AddonManager(CoreSysAttributes):
],
on_condition=AddonsJobError,
)
async def rebuild(self, slug: str) -> asyncio.Task | None:
async def rebuild(self, slug: str, *, force: bool = False) -> asyncio.Task | None:
"""Perform a rebuild of local build add-on.
Returns a Task that completes when addon has state 'started' (see addon.start)
@@ -289,8 +305,8 @@ class AddonManager(CoreSysAttributes):
raise AddonsError(
"Version changed, use Update instead Rebuild", _LOGGER.error
)
if not addon.need_build:
raise AddonsNotSupportedError(
if not force and not addon.need_build:
raise AddonNotSupportedError(
"Can't rebuild a image based add-on", _LOGGER.error
)
@@ -334,8 +350,7 @@ class AddonManager(CoreSysAttributes):
# Update ingress
if had_ingress != addon.ingress_panel:
await self.sys_ingress.reload()
with suppress(HomeAssistantAPIError):
await self.sys_ingress.update_hass_panel(addon)
await self.sys_ingress.update_hass_panel(addon)
return wait_for_start

View File

@@ -89,7 +89,12 @@ from ..const import (
)
from ..coresys import CoreSys
from ..docker.const import Capabilities
from ..exceptions import AddonsNotSupportedError
from ..exceptions import (
AddonNotSupportedArchitectureError,
AddonNotSupportedError,
AddonNotSupportedHomeAssistantVersionError,
AddonNotSupportedMachineTypeError,
)
from ..jobs.const import JOB_GROUP_ADDON
from ..jobs.job_group import JobGroup
from ..utils import version_is_new_enough
@@ -645,7 +650,7 @@ class AddonModel(JobGroup, ABC):
return None
# Return data
return readme.read_text(encoding="utf-8")
return readme.read_text(encoding="utf-8", errors="replace")
return await self.sys_run_in_executor(read_readme)
@@ -680,9 +685,8 @@ class AddonModel(JobGroup, ABC):
"""Validate if addon is available for current system."""
# Architecture
if not self.sys_arch.is_supported(config[ATTR_ARCH]):
raise AddonsNotSupportedError(
f"Add-on {self.slug} not supported on this platform, supported architectures: {', '.join(config[ATTR_ARCH])}",
logger,
raise AddonNotSupportedArchitectureError(
logger, slug=self.slug, architectures=config[ATTR_ARCH]
)
# Machine / Hardware
@@ -690,9 +694,8 @@ class AddonModel(JobGroup, ABC):
if machine and (
f"!{self.sys_machine}" in machine or self.sys_machine not in machine
):
raise AddonsNotSupportedError(
f"Add-on {self.slug} not supported on this machine, supported machine types: {', '.join(machine)}",
logger,
raise AddonNotSupportedMachineTypeError(
logger, slug=self.slug, machine_types=machine
)
# Home Assistant
@@ -701,16 +704,15 @@ class AddonModel(JobGroup, ABC):
if version and not version_is_new_enough(
self.sys_homeassistant.version, version
):
raise AddonsNotSupportedError(
f"Add-on {self.slug} not supported on this system, requires Home Assistant version {version} or greater",
logger,
raise AddonNotSupportedHomeAssistantVersionError(
logger, slug=self.slug, version=str(version)
)
def _available(self, config) -> bool:
"""Return True if this add-on is available on this platform."""
try:
self._validate_availability(config)
except AddonsNotSupportedError:
except AddonNotSupportedError:
return False
return True

View File

@@ -93,15 +93,7 @@ class AddonOptions(CoreSysAttributes):
typ = self.raw_schema[key]
try:
if isinstance(typ, list):
# nested value list
options[key] = self._nested_validate_list(typ[0], value, key)
elif isinstance(typ, dict):
# nested value dict
options[key] = self._nested_validate_dict(typ, value, key)
else:
# normal value
options[key] = self._single_validate(typ, value, key)
options[key] = self._validate_element(typ, value, key)
except (IndexError, KeyError):
raise vol.Invalid(
f"Type error for option '{key}' in {self._name} ({self._slug})"
@@ -111,7 +103,20 @@ class AddonOptions(CoreSysAttributes):
return options
# pylint: disable=no-value-for-parameter
def _single_validate(self, typ: str, value: Any, key: str):
def _validate_element(self, typ: Any, value: Any, key: str) -> Any:
"""Validate a value against a type specification."""
if isinstance(typ, list):
# nested value list
return self._nested_validate_list(typ[0], value, key)
elif isinstance(typ, dict):
# nested value dict
return self._nested_validate_dict(typ, value, key)
else:
# normal value
return self._single_validate(typ, value, key)
# pylint: disable=no-value-for-parameter
def _single_validate(self, typ: str, value: Any, key: str) -> Any:
"""Validate a single element."""
# if required argument
if value is None:
@@ -182,13 +187,15 @@ class AddonOptions(CoreSysAttributes):
# Device valid
self.devices.add(device)
return str(device.path)
return str(value)
raise vol.Invalid(
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
) -> list[Any]:
"""Validate nested items."""
options = []
@@ -201,17 +208,13 @@ class AddonOptions(CoreSysAttributes):
# Process list
for element in data_list:
# Nested?
if isinstance(typ, dict):
c_options = self._nested_validate_dict(typ, element, key)
options.append(c_options)
else:
options.append(self._single_validate(typ, element, key))
options.append(self._validate_element(typ, element, key))
return options
def _nested_validate_dict(
self, typ: dict[Any, Any], data_dict: dict[Any, Any], key: str
):
) -> dict[Any, Any]:
"""Validate nested items."""
options = {}
@@ -231,12 +234,7 @@ class AddonOptions(CoreSysAttributes):
continue
# Nested?
if isinstance(typ[c_key], list):
options[c_key] = self._nested_validate_list(
typ[c_key][0], c_value, c_key
)
else:
options[c_key] = self._single_validate(typ[c_key], c_value, c_key)
options[c_key] = self._validate_element(typ[c_key], c_value, c_key)
self._check_missing_options(typ, options, key)
return options
@@ -274,18 +272,28 @@ class UiOptions(CoreSysAttributes):
# read options
for key, value in raw_schema.items():
if isinstance(value, list):
# nested value list
self._nested_ui_list(ui_schema, value, key)
elif isinstance(value, dict):
# nested value dict
self._nested_ui_dict(ui_schema, value, key)
else:
# normal value
self._single_ui_option(ui_schema, value, key)
self._ui_schema_element(ui_schema, value, key)
return ui_schema
def _ui_schema_element(
self,
ui_schema: list[dict[str, Any]],
value: str,
key: str,
multiple: bool = False,
):
if isinstance(value, list):
# nested value list
assert not multiple
self._nested_ui_list(ui_schema, value, key)
elif isinstance(value, dict):
# nested value dict
self._nested_ui_dict(ui_schema, value, key, multiple)
else:
# normal value
self._single_ui_option(ui_schema, value, key, multiple)
def _single_ui_option(
self,
ui_schema: list[dict[str, Any]],
@@ -377,10 +385,7 @@ class UiOptions(CoreSysAttributes):
_LOGGER.error("Invalid schema %s", key)
return
if isinstance(element, dict):
self._nested_ui_dict(ui_schema, element, key, multiple=True)
else:
self._single_ui_option(ui_schema, element, key, multiple=True)
self._ui_schema_element(ui_schema, element, key, multiple=True)
def _nested_ui_dict(
self,
@@ -399,11 +404,7 @@ class UiOptions(CoreSysAttributes):
nested_schema: list[dict[str, Any]] = []
for c_key, c_value in option_dict.items():
# Nested?
if isinstance(c_value, list):
self._nested_ui_list(nested_schema, c_value, c_key)
else:
self._single_ui_option(nested_schema, c_value, c_key)
self._ui_schema_element(nested_schema, c_value, c_key)
ui_node["schema"] = nested_schema
ui_schema.append(ui_node)

View File

@@ -32,6 +32,7 @@ from ..const import (
ATTR_DISCOVERY,
ATTR_DOCKER_API,
ATTR_ENVIRONMENT,
ATTR_FIELDS,
ATTR_FULL_ACCESS,
ATTR_GPIO,
ATTR_HASSIO_API,
@@ -137,7 +138,19 @@ RE_DOCKER_IMAGE_BUILD = re.compile(
r"^([a-zA-Z\-\.:\d{}]+/)*?([\-\w{}]+)/([\-\w{}]+)(:[\.\-\w{}]+)?$"
)
SCHEMA_ELEMENT = vol.Match(RE_SCHEMA_ELEMENT)
SCHEMA_ELEMENT = vol.Schema(
vol.Any(
vol.Match(RE_SCHEMA_ELEMENT),
[
# A list may not directly contain another list
vol.Any(
vol.Match(RE_SCHEMA_ELEMENT),
{str: vol.Self},
)
],
{str: vol.Self},
)
)
RE_MACHINE = re.compile(
r"^!?(?:"
@@ -266,10 +279,23 @@ def _migrate_addon_config(protocol=False):
volumes = []
for entry in config.get(ATTR_MAP, []):
if isinstance(entry, dict):
# Validate that dict entries have required 'type' field
if ATTR_TYPE not in entry:
_LOGGER.warning(
"Add-on config has invalid map entry missing 'type' field: %s. Skipping invalid entry for %s",
entry,
name,
)
continue
volumes.append(entry)
if isinstance(entry, str):
result = RE_VOLUME.match(entry)
if not result:
_LOGGER.warning(
"Add-on config has invalid map entry: %s. Skipping invalid entry for %s",
entry,
name,
)
continue
volumes.append(
{
@@ -278,8 +304,8 @@ def _migrate_addon_config(protocol=False):
}
)
if volumes:
config[ATTR_MAP] = volumes
# Always update config to clear potentially malformed ones
config[ATTR_MAP] = volumes
# 2023-10 "config" became "homeassistant" so /config can be used for addon's public config
if any(volume[ATTR_TYPE] == MappingType.CONFIG for volume in volumes):
@@ -393,20 +419,7 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
vol.Optional(ATTR_CODENOTARY): vol.Email(),
vol.Optional(ATTR_OPTIONS, default={}): dict,
vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
vol.Schema(
{
str: vol.Any(
SCHEMA_ELEMENT,
[
vol.Any(
SCHEMA_ELEMENT,
{str: vol.Any(SCHEMA_ELEMENT, [SCHEMA_ELEMENT])},
)
],
vol.Schema({str: vol.Any(SCHEMA_ELEMENT, [SCHEMA_ELEMENT])}),
)
}
),
vol.Schema({str: SCHEMA_ELEMENT}),
False,
),
vol.Optional(ATTR_IMAGE): docker_image,
@@ -442,6 +455,7 @@ SCHEMA_TRANSLATION_CONFIGURATION = vol.Schema(
{
vol.Required(ATTR_NAME): str,
vol.Optional(ATTR_DESCRIPTON): vol.Maybe(str),
vol.Optional(ATTR_FIELDS): {str: vol.Self},
},
extra=vol.REMOVE_EXTRA,
)

View File

@@ -146,6 +146,14 @@ class RestAPI(CoreSysAttributes):
follow=True,
),
),
web.get(
f"{path}/logs/latest",
partial(
self._api_host.advanced_logs,
identifier=syslog_identifier,
latest=True,
),
),
web.get(
f"{path}/logs/boots/{{bootid}}",
partial(self._api_host.advanced_logs, identifier=syslog_identifier),
@@ -198,6 +206,7 @@ class RestAPI(CoreSysAttributes):
web.post("/host/reload", api_host.reload),
web.post("/host/options", api_host.options),
web.get("/host/services", api_host.services),
web.get("/host/disks/default/usage", api_host.disk_usage),
]
)
@@ -439,6 +448,7 @@ class RestAPI(CoreSysAttributes):
# is known and reported to the user using the resolution center.
await async_capture_exception(err)
kwargs.pop("follow", None) # Follow is not supported for Docker logs
kwargs.pop("latest", None) # Latest is not supported for Docker logs
return await api_supervisor.logs(*args, **kwargs)
self.webapp.add_routes(
@@ -448,6 +458,10 @@ class RestAPI(CoreSysAttributes):
"/supervisor/logs/follow",
partial(get_supervisor_logs, follow=True),
),
web.get(
"/supervisor/logs/latest",
partial(get_supervisor_logs, latest=True),
),
web.get("/supervisor/logs/boots/{bootid}", get_supervisor_logs),
web.get(
"/supervisor/logs/boots/{bootid}/follow",
@@ -560,6 +574,10 @@ class RestAPI(CoreSysAttributes):
"/addons/{addon}/logs/follow",
partial(get_addon_logs, follow=True),
),
web.get(
"/addons/{addon}/logs/latest",
partial(get_addon_logs, latest=True),
),
web.get("/addons/{addon}/logs/boots/{bootid}", get_addon_logs),
web.get(
"/addons/{addon}/logs/boots/{bootid}/follow",
@@ -734,6 +752,10 @@ class RestAPI(CoreSysAttributes):
"/store/addons/{addon}/documentation",
api_store.addons_addon_documentation,
),
web.get(
"/store/addons/{addon}/availability",
api_store.addons_addon_availability,
),
web.post(
"/store/addons/{addon}/install", api_store.addons_addon_install
),

View File

@@ -36,6 +36,7 @@ from ..const import (
ATTR_DNS,
ATTR_DOCKER_API,
ATTR_DOCUMENTATION,
ATTR_FORCE,
ATTR_FULL_ACCESS,
ATTR_GPIO,
ATTR_HASSIO_API,
@@ -139,6 +140,8 @@ SCHEMA_SECURITY = vol.Schema({vol.Optional(ATTR_PROTECTED): vol.Boolean()})
SCHEMA_UNINSTALL = vol.Schema(
{vol.Optional(ATTR_REMOVE_CONFIG, default=False): vol.Boolean()}
)
SCHEMA_REBUILD = vol.Schema({vol.Optional(ATTR_FORCE, default=False): vol.Boolean()})
# pylint: enable=no-value-for-parameter
@@ -303,7 +306,7 @@ class APIAddons(CoreSysAttributes):
)
# Validate/Process Body
body = await api_validate(addon_schema, request, origin=[ATTR_OPTIONS])
body = await api_validate(addon_schema, request)
if ATTR_OPTIONS in body:
addon.options = body[ATTR_OPTIONS]
if ATTR_BOOT in body:
@@ -461,7 +464,11 @@ class APIAddons(CoreSysAttributes):
async def rebuild(self, request: web.Request) -> None:
"""Rebuild local build add-on."""
addon = self.get_addon_for_request(request)
if start_task := await asyncio.shield(self.sys_addons.rebuild(addon.slug)):
body: dict[str, Any] = await api_validate(SCHEMA_REBUILD, request)
if start_task := await asyncio.shield(
self.sys_addons.rebuild(addon.slug, force=body[ATTR_FORCE])
):
await start_task
@api_process

View File

@@ -92,13 +92,18 @@ class APIAuth(CoreSysAttributes):
# Json
if request.headers.get(CONTENT_TYPE) == CONTENT_TYPE_JSON:
data = await request.json(loads=json_loads)
return await self._process_dict(request, addon, data)
if not await self._process_dict(request, addon, data):
raise HTTPUnauthorized()
return True
# URL encoded
if request.headers.get(CONTENT_TYPE) == CONTENT_TYPE_URL:
data = await request.post()
return await self._process_dict(request, addon, data)
if not await self._process_dict(request, addon, data):
raise HTTPUnauthorized()
return True
# Advertise Basic authentication by default
raise HTTPUnauthorized(headers=REALM_HEADER)
@api_process

View File

@@ -3,7 +3,6 @@
from __future__ import annotations
import asyncio
from collections.abc import Callable
import errno
from io import IOBase
import logging
@@ -46,12 +45,9 @@ from ..const import (
ATTR_TYPE,
ATTR_VERSION,
REQUEST_FROM,
BusEvent,
CoreState,
)
from ..coresys import CoreSysAttributes
from ..exceptions import APIError, APIForbidden, APINotFound
from ..jobs import JobSchedulerOptions, SupervisorJob
from ..mounts.const import MountUsage
from ..resolution.const import UnhealthyReason
from .const import (
@@ -61,7 +57,7 @@ from .const import (
ATTR_LOCATIONS,
CONTENT_TYPE_TAR,
)
from .utils import api_process, api_validate
from .utils import api_process, api_validate, background_task
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -289,41 +285,6 @@ class APIBackups(CoreSysAttributes):
f"Location {LOCATION_CLOUD_BACKUP} is only available for Home Assistant"
)
async def _background_backup_task(
self, backup_method: Callable, *args, **kwargs
) -> tuple[asyncio.Task, str]:
"""Start backup task in background and return task and job ID."""
event = asyncio.Event()
job, backup_task = cast(
tuple[SupervisorJob, asyncio.Task],
self.sys_jobs.schedule_job(
backup_method, JobSchedulerOptions(), *args, **kwargs
),
)
async def release_on_freeze(new_state: CoreState):
if new_state == CoreState.FREEZE:
event.set()
# Wait for system to get into freeze state before returning
# If the backup fails validation it will raise before getting there
listener = self.sys_bus.register_event(
BusEvent.SUPERVISOR_STATE_CHANGE, release_on_freeze
)
try:
event_task = self.sys_create_task(event.wait())
_, pending = await asyncio.wait(
(backup_task, event_task),
return_when=asyncio.FIRST_COMPLETED,
)
# It seems backup returned early (error or something), make sure to cancel
# the event task to avoid "Task was destroyed but it is pending!" errors.
if event_task in pending:
event_task.cancel()
return (backup_task, job.uuid)
finally:
self.sys_bus.remove_listener(listener)
@api_process
async def backup_full(self, request: web.Request):
"""Create full backup."""
@@ -342,8 +303,8 @@ class APIBackups(CoreSysAttributes):
body[ATTR_ADDITIONAL_LOCATIONS] = locations
background = body.pop(ATTR_BACKGROUND)
backup_task, job_id = await self._background_backup_task(
self.sys_backups.do_backup_full, **body
backup_task, job_id = await background_task(
self, self.sys_backups.do_backup_full, **body
)
if background and not backup_task.done():
@@ -378,8 +339,8 @@ class APIBackups(CoreSysAttributes):
body[ATTR_ADDONS] = list(self.sys_addons.local)
background = body.pop(ATTR_BACKGROUND)
backup_task, job_id = await self._background_backup_task(
self.sys_backups.do_backup_partial, **body
backup_task, job_id = await background_task(
self, self.sys_backups.do_backup_partial, **body
)
if background and not backup_task.done():
@@ -402,8 +363,8 @@ class APIBackups(CoreSysAttributes):
request, body.get(ATTR_LOCATION, backup.location)
)
background = body.pop(ATTR_BACKGROUND)
restore_task, job_id = await self._background_backup_task(
self.sys_backups.do_restore_full, backup, **body
restore_task, job_id = await background_task(
self, self.sys_backups.do_restore_full, backup, **body
)
if background and not restore_task.done() or await restore_task:
@@ -422,8 +383,8 @@ class APIBackups(CoreSysAttributes):
request, body.get(ATTR_LOCATION, backup.location)
)
background = body.pop(ATTR_BACKGROUND)
restore_task, job_id = await self._background_backup_task(
self.sys_backups.do_restore_partial, backup, **body
restore_task, job_id = await background_task(
self, self.sys_backups.do_restore_partial, backup, **body
)
if background and not restore_task.done() or await restore_task:

View File

@@ -49,6 +49,7 @@ ATTR_LLMNR_HOSTNAME = "llmnr_hostname"
ATTR_LOCAL_ONLY = "local_only"
ATTR_LOCATION_ATTRIBUTES = "location_attributes"
ATTR_LOCATIONS = "locations"
ATTR_MAX_DEPTH = "max_depth"
ATTR_MDNS = "mdns"
ATTR_MODEL = "model"
ATTR_MOUNTS = "mounts"

View File

@@ -6,10 +6,13 @@ from typing import Any
from aiohttp import web
import voluptuous as vol
from supervisor.resolution.const import ContextType, IssueType, SuggestionType
from ..const import (
ATTR_ENABLE_IPV6,
ATTR_HOSTNAME,
ATTR_LOGGING,
ATTR_MTU,
ATTR_PASSWORD,
ATTR_REGISTRIES,
ATTR_STORAGE,
@@ -32,7 +35,12 @@ SCHEMA_DOCKER_REGISTRY = vol.Schema(
)
# pylint: disable=no-value-for-parameter
SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_ENABLE_IPV6): vol.Boolean()})
SCHEMA_OPTIONS = vol.Schema(
{
vol.Optional(ATTR_ENABLE_IPV6): vol.Maybe(vol.Boolean()),
vol.Optional(ATTR_MTU): vol.Maybe(vol.All(int, vol.Range(min=68, max=65535))),
}
)
class APIDocker(CoreSysAttributes):
@@ -49,6 +57,7 @@ class APIDocker(CoreSysAttributes):
return {
ATTR_VERSION: self.sys_docker.info.version,
ATTR_ENABLE_IPV6: self.sys_docker.config.enable_ipv6,
ATTR_MTU: self.sys_docker.config.mtu,
ATTR_STORAGE: self.sys_docker.info.storage,
ATTR_LOGGING: self.sys_docker.info.logging,
ATTR_REGISTRIES: data_registries,
@@ -59,8 +68,28 @@ class APIDocker(CoreSysAttributes):
"""Set docker options."""
body = await api_validate(SCHEMA_OPTIONS, request)
if ATTR_ENABLE_IPV6 in body:
reboot_required = False
if (
ATTR_ENABLE_IPV6 in body
and self.sys_docker.config.enable_ipv6 != body[ATTR_ENABLE_IPV6]
):
self.sys_docker.config.enable_ipv6 = body[ATTR_ENABLE_IPV6]
reboot_required = True
if ATTR_MTU in body and self.sys_docker.config.mtu != body[ATTR_MTU]:
self.sys_docker.config.mtu = body[ATTR_MTU]
reboot_required = True
if reboot_required:
_LOGGER.info(
"Host system reboot required to apply Docker configuration changes"
)
self.sys_resolution.create_issue(
IssueType.REBOOT_REQUIRED,
ContextType.SYSTEM,
suggestions=[SuggestionType.EXECUTE_REBOOT],
)
await self.sys_docker.config.save_data()

View File

@@ -20,6 +20,7 @@ from ..const import (
ATTR_CPU_PERCENT,
ATTR_IMAGE,
ATTR_IP_ADDRESS,
ATTR_JOB_ID,
ATTR_MACHINE,
ATTR_MEMORY_LIMIT,
ATTR_MEMORY_PERCENT,
@@ -37,8 +38,8 @@ from ..const import (
from ..coresys import CoreSysAttributes
from ..exceptions import APIDBMigrationInProgress, APIError
from ..validate import docker_image, network_port, version_tag
from .const import ATTR_FORCE, ATTR_SAFE_MODE
from .utils import api_process, api_validate
from .const import ATTR_BACKGROUND, ATTR_FORCE, ATTR_SAFE_MODE
from .utils import api_process, api_validate, background_task
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -61,6 +62,7 @@ SCHEMA_UPDATE = vol.Schema(
{
vol.Optional(ATTR_VERSION): version_tag,
vol.Optional(ATTR_BACKUP): bool,
vol.Optional(ATTR_BACKGROUND, default=False): bool,
}
)
@@ -170,18 +172,24 @@ class APIHomeAssistant(CoreSysAttributes):
}
@api_process
async def update(self, request: web.Request) -> None:
async def update(self, request: web.Request) -> dict[str, str] | None:
"""Update Home Assistant."""
body = await api_validate(SCHEMA_UPDATE, request)
await self._check_offline_migration()
await asyncio.shield(
self.sys_homeassistant.core.update(
version=body.get(ATTR_VERSION, self.sys_homeassistant.latest_version),
backup=body.get(ATTR_BACKUP),
)
background = body[ATTR_BACKGROUND]
update_task, job_id = await background_task(
self,
self.sys_homeassistant.core.update,
version=body.get(ATTR_VERSION, self.sys_homeassistant.latest_version),
backup=body.get(ATTR_BACKUP),
)
if background and not update_task.done():
return {ATTR_JOB_ID: job_id}
return await update_task
@api_process
async def stop(self, request: web.Request) -> Awaitable[None]:
"""Stop Home Assistant."""

View File

@@ -2,10 +2,17 @@
import asyncio
from contextlib import suppress
import json
import logging
from typing import Any
from aiohttp import ClientConnectionResetError, ClientPayloadError, web
from aiohttp import (
ClientConnectionResetError,
ClientError,
ClientPayloadError,
ClientTimeout,
web,
)
from aiohttp.hdrs import ACCEPT, RANGE
import voluptuous as vol
from voluptuous.error import CoerceInvalid
@@ -51,6 +58,7 @@ from .const import (
ATTR_FORCE,
ATTR_IDENTIFIERS,
ATTR_LLMNR_HOSTNAME,
ATTR_MAX_DEPTH,
ATTR_STARTUP_TIME,
ATTR_USE_NTP,
ATTR_VIRTUALIZATION,
@@ -193,7 +201,11 @@ class APIHost(CoreSysAttributes):
return possible_offset
async def advanced_logs_handler(
self, request: web.Request, identifier: str | None = None, follow: bool = False
self,
request: web.Request,
identifier: str | None = None,
follow: bool = False,
latest: bool = False,
) -> web.StreamResponse:
"""Return systemd-journald logs."""
log_formatter = LogFormatter.PLAIN
@@ -212,6 +224,20 @@ class APIHost(CoreSysAttributes):
if follow:
params[PARAM_FOLLOW] = ""
if latest:
if not identifier:
raise APIError(
"Latest logs can only be fetched for a specific identifier."
)
try:
epoch = await self._get_container_last_epoch(identifier)
params["CONTAINER_LOG_EPOCH"] = epoch
except HostLogError as err:
raise APIError(
f"Cannot determine CONTAINER_LOG_EPOCH of {identifier}, latest logs not available."
) from err
if ACCEPT in request.headers and request.headers[ACCEPT] not in [
CONTENT_TYPE_TEXT,
CONTENT_TYPE_X_LOG,
@@ -240,6 +266,8 @@ class APIHost(CoreSysAttributes):
lines = max(2, lines)
# entries=cursor[[:num_skip]:num_entries]
range_header = f"entries=:-{lines - 1}:{SYSTEMD_JOURNAL_GATEWAYD_LINES_MAX if follow else lines}"
elif latest:
range_header = f"entries=:0:{SYSTEMD_JOURNAL_GATEWAYD_LINES_MAX}"
elif RANGE in request.headers:
range_header = request.headers[RANGE]
else:
@@ -285,7 +313,81 @@ class APIHost(CoreSysAttributes):
@api_process_raw(CONTENT_TYPE_TEXT, error_type=CONTENT_TYPE_TEXT)
async def advanced_logs(
self, request: web.Request, identifier: str | None = None, follow: bool = False
self,
request: web.Request,
identifier: str | None = None,
follow: bool = False,
latest: bool = False,
) -> web.StreamResponse:
"""Return systemd-journald logs. Wrapped as standard API handler."""
return await self.advanced_logs_handler(request, identifier, follow)
return await self.advanced_logs_handler(request, identifier, follow, latest)
@api_process
async def disk_usage(self, request: web.Request) -> dict:
"""Return a breakdown of storage usage for the system."""
max_depth = request.query.get(ATTR_MAX_DEPTH, 1)
try:
max_depth = int(max_depth)
except ValueError:
max_depth = 1
disk = self.sys_hardware.disk
total, used, _ = await self.sys_run_in_executor(
disk.disk_usage, self.sys_config.path_supervisor
)
known_paths = await self.sys_run_in_executor(
disk.get_dir_sizes,
{
"addons_data": self.sys_config.path_addons_data,
"addons_config": self.sys_config.path_addon_configs,
"media": self.sys_config.path_media,
"share": self.sys_config.path_share,
"backup": self.sys_config.path_backup,
"ssl": self.sys_config.path_ssl,
"homeassistant": self.sys_config.path_homeassistant,
},
max_depth,
)
return {
# this can be the disk/partition ID in the future
"id": "root",
"label": "Root",
"total_bytes": total,
"used_bytes": used,
"children": [
{
"id": "system",
"label": "System",
"used_bytes": used
- sum(path["used_bytes"] for path in known_paths),
},
*known_paths,
],
}
async def _get_container_last_epoch(self, identifier: str) -> str | None:
"""Get Docker's internal log epoch of the latest log entry for the given identifier."""
try:
async with self.sys_host.logs.journald_logs(
params={"CONTAINER_NAME": identifier},
range_header="entries=:-1:2", # -1 = next to the last entry
accept=LogFormat.JSON,
timeout=ClientTimeout(total=10),
) as resp:
text = await resp.text()
except (ClientError, TimeoutError) as err:
raise HostLogError(
"Could not get last container epoch from systemd-journal-gatewayd",
_LOGGER.error,
) from err
try:
return json.loads(text.strip().split("\n")[-1])["CONTAINER_LOG_EPOCH"]
except (json.JSONDecodeError, KeyError, IndexError) as err:
raise HostLogError(
f"Failed to parse CONTAINER_LOG_EPOCH of {identifier} container, got: {text}",
_LOGGER.error,
) from err

View File

@@ -199,21 +199,25 @@ class APIIngress(CoreSysAttributes):
url = f"{url}?{request.query_string}"
# Start proxy
async with self.sys_websession.ws_connect(
url,
headers=source_header,
protocols=req_protocols,
autoclose=False,
autoping=False,
) as ws_client:
# Proxy requests
await asyncio.wait(
[
self.sys_create_task(_websocket_forward(ws_server, ws_client)),
self.sys_create_task(_websocket_forward(ws_client, ws_server)),
],
return_when=asyncio.FIRST_COMPLETED,
)
try:
_LOGGER.debug("Proxing WebSocket to %s, upstream url: %s", addon.slug, url)
async with self.sys_websession.ws_connect(
url,
headers=source_header,
protocols=req_protocols,
autoclose=False,
autoping=False,
) as ws_client:
# Proxy requests
await asyncio.wait(
[
self.sys_create_task(_websocket_forward(ws_server, ws_client)),
self.sys_create_task(_websocket_forward(ws_client, ws_server)),
],
return_when=asyncio.FIRST_COMPLETED,
)
except TimeoutError:
_LOGGER.warning("WebSocket proxy to %s timed out", addon.slug)
return ws_server
@@ -286,6 +290,7 @@ class APIIngress(CoreSysAttributes):
aiohttp.ClientError,
aiohttp.ClientPayloadError,
ConnectionResetError,
ConnectionError,
) as err:
_LOGGER.error("Stream error with %s: %s", url, err)
@@ -309,9 +314,9 @@ class APIIngress(CoreSysAttributes):
def _init_header(
request: web.Request, addon: Addon, session_data: IngressSessionData | None
) -> CIMultiDict | dict[str, str]:
) -> CIMultiDict[str]:
"""Create initial header."""
headers = {}
headers = CIMultiDict[str]()
if session_data is not None:
headers[HEADER_REMOTE_USER_ID] = session_data.user.id
@@ -337,7 +342,7 @@ def _init_header(
istr(HEADER_REMOTE_USER_DISPLAY_NAME),
):
continue
headers[name] = value
headers.add(name, value)
# Update X-Forwarded-For
if request.transport:
@@ -348,9 +353,9 @@ def _init_header(
return headers
def _response_header(response: aiohttp.ClientResponse) -> dict[str, str]:
def _response_header(response: aiohttp.ClientResponse) -> CIMultiDict[str]:
"""Create response header."""
headers = {}
headers = CIMultiDict[str]()
for name, value in response.headers.items():
if name in (
@@ -360,7 +365,7 @@ def _response_header(response: aiohttp.ClientResponse) -> dict[str, str]:
hdrs.CONTENT_ENCODING,
):
continue
headers[name] = value
headers.add(name, value)
return headers
@@ -386,9 +391,9 @@ async def _websocket_forward(ws_from, ws_to):
elif msg.type == aiohttp.WSMsgType.BINARY:
await ws_to.send_bytes(msg.data)
elif msg.type == aiohttp.WSMsgType.PING:
await ws_to.ping()
await ws_to.ping(msg.data)
elif msg.type == aiohttp.WSMsgType.PONG:
await ws_to.pong()
await ws_to.pong(msg.data)
elif ws_to.closed:
await ws_to.close(code=ws_to.close_code, message=msg.extra)
except RuntimeError:

View File

@@ -26,7 +26,9 @@ from ..const import (
ATTR_IP6_PRIVACY,
ATTR_IPV4,
ATTR_IPV6,
ATTR_LLMNR,
ATTR_MAC,
ATTR_MDNS,
ATTR_METHOD,
ATTR_MODE,
ATTR_NAMESERVERS,
@@ -54,6 +56,7 @@ from ..host.configuration import (
Ip6Setting,
IpConfig,
IpSetting,
MulticastDnsMode,
VlanConfig,
WifiConfig,
)
@@ -97,6 +100,8 @@ SCHEMA_UPDATE = vol.Schema(
vol.Optional(ATTR_IPV6): _SCHEMA_IPV6_CONFIG,
vol.Optional(ATTR_WIFI): _SCHEMA_WIFI_CONFIG,
vol.Optional(ATTR_ENABLED): vol.Boolean(),
vol.Optional(ATTR_MDNS): vol.Coerce(MulticastDnsMode),
vol.Optional(ATTR_LLMNR): vol.Coerce(MulticastDnsMode),
}
)
@@ -160,6 +165,8 @@ def interface_struct(interface: Interface) -> dict[str, Any]:
else None,
ATTR_WIFI: wifi_struct(interface.wifi) if interface.wifi else None,
ATTR_VLAN: vlan_struct(interface.vlan) if interface.vlan else None,
ATTR_MDNS: interface.mdns,
ATTR_LLMNR: interface.llmnr,
}
@@ -260,6 +267,10 @@ class APINetwork(CoreSysAttributes):
)
elif key == ATTR_ENABLED:
interface.enabled = config
elif key == ATTR_MDNS:
interface.mdns = config
elif key == ATTR_LLMNR:
interface.llmnr = config
await asyncio.shield(self.sys_host.network.apply_changes(interface))
@@ -300,6 +311,15 @@ class APINetwork(CoreSysAttributes):
vlan_config = VlanConfig(vlan, interface.name)
mdns_mode = MulticastDnsMode.DEFAULT
llmnr_mode = MulticastDnsMode.DEFAULT
if ATTR_MDNS in body:
mdns_mode = body[ATTR_MDNS]
if ATTR_LLMNR in body:
llmnr_mode = body[ATTR_LLMNR]
ipv4_setting = None
if ATTR_IPV4 in body:
ipv4_setting = IpSetting(
@@ -325,7 +345,7 @@ class APINetwork(CoreSysAttributes):
)
vlan_interface = Interface(
"",
f"{interface.name}.{vlan}",
"",
"",
True,
@@ -338,5 +358,7 @@ class APINetwork(CoreSysAttributes):
ipv6_setting,
None,
vlan_config,
mdns=mdns_mode,
llmnr=llmnr_mode,
)
await asyncio.shield(self.sys_host.network.apply_changes(vlan_interface))
await asyncio.shield(self.sys_host.network.create_vlan(vlan_interface))

View File

@@ -1 +1 @@
!function(){function d(d){var e=document.createElement("script");e.src=d,document.body.appendChild(e)}if(/Edge?\/(12[4-9]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Firefox\/(12[5-9]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Chrom(ium|e)\/(109|1[1-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|(Maci|X1{2}).+ Version\/(17\.([5-9]|\d{2,})|(1[89]|[2-9]\d|\d{3,})\.\d+)([,.]\d+|)( \(\w+\)|)( Mobile\/\w+|) Safari\/|Chrome.+OPR\/(1{2}\d|1[2-9]\d|[2-9]\d{2}|\d{4,})\.\d+\.\d+|(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS|CPU iPad OS)[ +]+(15[._]([6-9]|\d{2,})|(1[6-9]|[2-9]\d|\d{3,})[._]\d+)([._]\d+|)|Android:?[ /-](12[4-9]|1[3-9]\d|[2-9]\d{2}|\d{4,})(\.\d+|)(\.\d+|)|Mobile Safari.+OPR\/([89]\d|\d{3,})\.\d+\.\d+|Android.+Firefox\/(12[5-9]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Android.+Chrom(ium|e)\/(12[4-9]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|SamsungBrowser\/(2[5-9]|[3-9]\d|\d{3,})\.\d+|Home As{2}istant\/[\d.]+ \(.+; macOS (1[2-9]|[2-9]\d|\d{3,})\.\d+(\.\d+)?\)/.test(navigator.userAgent))try{new Function("import('/api/hassio/app/frontend_latest/entrypoint.35399ae87c70acf8.js')")()}catch(e){d("/api/hassio/app/frontend_es5/entrypoint.476bfed22da63267.js")}else d("/api/hassio/app/frontend_es5/entrypoint.476bfed22da63267.js")}()
!function(){function d(d){var e=document.createElement("script");e.src=d,document.body.appendChild(e)}if(/Edge?\/(12[89]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Firefox\/(12[89]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Chrom(ium|e)\/(109|1[1-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|(Maci|X1{2}).+ Version\/(18\.\d+|(19|[2-9]\d|\d{3,})\.\d+)([,.]\d+|)( \(\w+\)|)( Mobile\/\w+|) Safari\/|Chrome.+OPR\/(1{2}[3-9]|1[2-9]\d|[2-9]\d{2}|\d{4,})\.\d+\.\d+|(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS|CPU iPad OS)[ +]+(16[._]([6-9]|\d{2,})|(1[7-9]|[2-9]\d|\d{3,})[._]\d+)([._]\d+|)|Android:?[ /-](12[89]|1[3-9]\d|[2-9]\d{2}|\d{4,})(\.\d+|)(\.\d+|)|Mobile Safari.+OPR\/([89]\d|\d{3,})\.\d+\.\d+|Android.+Firefox\/(13\d|1[4-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Android.+Chrom(ium|e)\/(12[89]|1[3-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|SamsungBrowser\/(2[7-9]|[3-9]\d|\d{3,})\.\d+|Home As{2}istant\/[\d.]+ \(.+; macOS (1[3-9]|[2-9]\d|\d{3,})\.\d+(\.\d+)?\)/.test(navigator.userAgent))try{new Function("import('/api/hassio/app/frontend_latest/entrypoint.13b942f66af14ae7.js')")()}catch(e){d("/api/hassio/app/frontend_es5/entrypoint.4fccc51701dd6142.js")}else d("/api/hassio/app/frontend_es5/entrypoint.4fccc51701dd6142.js")}()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
{"version":3,"file":"1081.91949d686e61cc12.js","sources":["https://raw.githubusercontent.com/home-assistant/frontend/20250401.0/src/components/ha-button-toggle-group.ts","https://raw.githubusercontent.com/home-assistant/frontend/20250401.0/src/components/ha-selector/ha-selector-button-toggle.ts"],"names":["_decorate","customElement","_initialize","_LitElement","F","constructor","args","d","kind","decorators","property","attribute","key","value","type","Boolean","queryAll","html","_t","_","this","buttons","map","button","iconPath","_t2","label","active","_handleClick","_t3","styleMap","width","fullWidth","length","dense","_this$_buttons","_buttons","forEach","async","updateComplete","shadowRoot","querySelector","style","margin","ev","currentTarget","fireEvent","static","css","_t4","LitElement","HaButtonToggleSelector","_this$selector$button","_this$selector$button2","_this$selector$button3","options","selector","button_toggle","option","translationKey","translation_key","localizeValue","localizedLabel","sort","a","b","caseInsensitiveStringCompare","hass","locale","language","toggleButtons","item","_valueChanged","_ev$detail","_this$value","stopPropagation","detail","target","disabled","undefined"],"mappings":"qXAWgCA,EAAAA,EAAAA,GAAA,EAD/BC,EAAAA,EAAAA,IAAc,4BAAyB,SAAAC,EAAAC,GAkIvC,OAAAC,EAlID,cACgCD,EAAoBE,WAAAA,IAAAC,GAAA,SAAAA,GAAAJ,EAAA,QAApBK,EAAA,EAAAC,KAAA,QAAAC,WAAA,EAC7BC,EAAAA,EAAAA,IAAS,CAAEC,WAAW,KAAQC,IAAA,UAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAE9BC,EAAAA,EAAAA,OAAUE,IAAA,SAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,IAAS,CAAEC,UAAW,aAAcG,KAAMC,WAAUH,IAAA,YAAAC,KAAAA,GAAA,OAClC,CAAK,IAAAL,KAAA,QAAAC,WAAA,EAEvBC,EAAAA,EAAAA,IAAS,CAAEI,KAAMC,WAAUH,IAAA,QAAAC,KAAAA,GAAA,OAAgB,CAAK,IAAAL,KAAA,QAAAC,WAAA,EAEhDO,EAAAA,EAAAA,IAAS,eAAaJ,IAAA,WAAAC,WAAA,IAAAL,KAAA,SAAAI,IAAA,SAAAC,MAEvB,WACE,OAAOI,EAAAA,EAAAA,IAAIC,IAAAA,EAAAC,CAAA,uBAELC,KAAKC,QAAQC,KAAKC,GAClBA,EAAOC,UACHP,EAAAA,EAAAA,IAAIQ,IAAAA,EAAAN,CAAA,2GACOI,EAAOG,MACRH,EAAOC,SACND,EAAOV,MACNO,KAAKO,SAAWJ,EAAOV,MACxBO,KAAKQ,eAEhBX,EAAAA,EAAAA,IAAIY,IAAAA,EAAAV,CAAA,iHACMW,EAAAA,EAAAA,GAAS,CACfC,MAAOX,KAAKY,UACL,IAAMZ,KAAKC,QAAQY,OAAtB,IACA,YAGGb,KAAKc,MACLX,EAAOV,MACNO,KAAKO,SAAWJ,EAAOV,MACxBO,KAAKQ,aACXL,EAAOG,SAKxB,GAAC,CAAAlB,KAAA,SAAAI,IAAA,UAAAC,MAED,WAAoB,IAAAsB,EAEL,QAAbA,EAAAf,KAAKgB,gBAAQ,IAAAD,GAAbA,EAAeE,SAAQC,gBACff,EAAOgB,eAEXhB,EAAOiB,WAAYC,cAAc,UACjCC,MAAMC,OAAS,GAAG,GAExB,GAAC,CAAAnC,KAAA,SAAAI,IAAA,eAAAC,MAED,SAAqB+B,GACnBxB,KAAKO,OAASiB,EAAGC,cAAchC,OAC/BiC,EAAAA,EAAAA,GAAU1B,KAAM,gBAAiB,CAAEP,MAAOO,KAAKO,QACjD,GAAC,CAAAnB,KAAA,QAAAuC,QAAA,EAAAnC,IAAA,SAAAC,KAAAA,GAAA,OAEemC,EAAAA,EAAAA,IAAGC,IAAAA,EAAA9B,CAAA,u0CAzDoB+B,EAAAA,I,MCD5BC,GAAsBnD,EAAAA,EAAAA,GAAA,EADlCC,EAAAA,EAAAA,IAAc,+BAA4B,SAAAC,EAAAC,GA4F1C,OAAAC,EA5FD,cACmCD,EAAoBE,WAAAA,IAAAC,GAAA,SAAAA,GAAAJ,EAAA,QAApBK,EAAA,EAAAC,KAAA,QAAAC,WAAA,EAChCC,EAAAA,EAAAA,IAAS,CAAEC,WAAW,KAAQC,IAAA,OAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAE9BC,EAAAA,EAAAA,IAAS,CAAEC,WAAW,KAAQC,IAAA,WAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAE9BC,EAAAA,EAAAA,OAAUE,IAAA,QAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,OAAUE,IAAA,QAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,OAAUE,IAAA,SAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,IAAS,CAAEC,WAAW,KAAQC,IAAA,gBAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAG9BC,EAAAA,EAAAA,IAAS,CAAEI,KAAMC,WAAUH,IAAA,WAAAC,KAAAA,GAAA,OAAmB,CAAK,IAAAL,KAAA,QAAAC,WAAA,EAEnDC,EAAAA,EAAAA,IAAS,CAAEI,KAAMC,WAAUH,IAAA,WAAAC,KAAAA,GAAA,OAAmB,CAAI,IAAAL,KAAA,SAAAI,IAAA,SAAAC,MAEnD,WAAmB,IAAAuC,EAAAC,EAAAC,EACjB,MAAMC,GACuB,QAA3BH,EAAAhC,KAAKoC,SAASC,qBAAa,IAAAL,GAAS,QAATA,EAA3BA,EAA6BG,eAAO,IAAAH,OAAA,EAApCA,EAAsC9B,KAAKoC,GACvB,iBAAXA,EACFA,EACA,CAAE7C,MAAO6C,EAAQhC,MAAOgC,OAC1B,GAEDC,EAA4C,QAA9BN,EAAGjC,KAAKoC,SAASC,qBAAa,IAAAJ,OAAA,EAA3BA,EAA6BO,gBAEhDxC,KAAKyC,eAAiBF,GACxBJ,EAAQlB,SAASqB,IACf,MAAMI,EAAiB1C,KAAKyC,cAC1B,GAAGF,aAA0BD,EAAO7C,SAElCiD,IACFJ,EAAOhC,MAAQoC,EACjB,IAI2B,QAA/BR,EAAIlC,KAAKoC,SAASC,qBAAa,IAAAH,GAA3BA,EAA6BS,MAC/BR,EAAQQ,MAAK,CAACC,EAAGC,KACfC,EAAAA,EAAAA,IACEF,EAAEtC,MACFuC,EAAEvC,MACFN,KAAK+C,KAAKC,OAAOC,YAKvB,MAAMC,EAAgCf,EAAQjC,KAAKiD,IAAkB,CACnE7C,MAAO6C,EAAK7C,MACZb,MAAO0D,EAAK1D,UAGd,OAAOI,EAAAA,EAAAA,IAAIC,IAAAA,EAAAC,CAAA,iHACPC,KAAKM,MAEM4C,EACDlD,KAAKP,MACEO,KAAKoD,cAG5B,GAAC,CAAAhE,KAAA,SAAAI,IAAA,gBAAAC,MAED,SAAsB+B,GAAI,IAAA6B,EAAAC,EACxB9B,EAAG+B,kBAEH,MAAM9D,GAAiB,QAAT4D,EAAA7B,EAAGgC,cAAM,IAAAH,OAAA,EAATA,EAAW5D,QAAS+B,EAAGiC,OAAOhE,MACxCO,KAAK0D,eAAsBC,IAAVlE,GAAuBA,KAAqB,QAAhB6D,EAAMtD,KAAKP,aAAK,IAAA6D,EAAAA,EAAI,MAGrE5B,EAAAA,EAAAA,GAAU1B,KAAM,gBAAiB,CAC/BP,MAAOA,GAEX,GAAC,CAAAL,KAAA,QAAAuC,QAAA,EAAAnC,IAAA,SAAAC,KAAAA,GAAA,OAEemC,EAAAA,EAAAA,IAAGvB,IAAAA,EAAAN,CAAA,wLA5EuB+B,EAAAA,G"}

View File

@@ -0,0 +1,2 @@
"use strict";(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([["113"],{51383:function(t,o,e){e.r(o),e.d(o,{HaIconButtonArrowNext:function(){return c}});e(26847),e(27530);var n=e(73742),a=e(59048),i=e(7616),r=e(88479);e(81777);let s,d=t=>t;class c extends a.oi{render(){var t;return(0,a.dy)(s||(s=d` <ha-icon-button .disabled="${0}" .label="${0}" .path="${0}"></ha-icon-button> `),this.disabled,this.label||(null===(t=this.hass)||void 0===t?void 0:t.localize("ui.common.next"))||"Next",this._icon)}constructor(...t){super(...t),this.disabled=!1,this._icon="rtl"===r.E.document.dir?"M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z":"M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z"}}(0,n.__decorate)([(0,i.Cb)({attribute:!1})],c.prototype,"hass",void 0),(0,n.__decorate)([(0,i.Cb)({type:Boolean})],c.prototype,"disabled",void 0),(0,n.__decorate)([(0,i.Cb)()],c.prototype,"label",void 0),(0,n.__decorate)([(0,i.SB)()],c.prototype,"_icon",void 0),c=(0,n.__decorate)([(0,i.Mo)("ha-icon-button-arrow-next")],c)}}]);
//# sourceMappingURL=113.13cc15fa81bd492f.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"113.13cc15fa81bd492f.js","sources":["https://raw.githubusercontent.com/home-assistant/frontend/20250811.0/src/components/ha-icon-button-arrow-next.ts"],"names":["HaIconButtonArrowNext","LitElement","render","_this$hass","html","_t","_","this","disabled","label","hass","localize","_icon","args","mainWindow","attribute","type","Boolean"],"mappings":"4RASO,MAAMA,UAA8BC,EAAAA,GAU/BC,MAAAA,GAAyB,IAAAC,EACjC,OAAOC,EAAAA,EAAAA,IAAIC,IAAAA,EAAAC,CAAA,mFAEKC,KAAKC,SACRD,KAAKE,QAAkB,QAAbN,EAAII,KAAKG,YAAI,IAAAP,OAAA,EAATA,EAAWQ,SAAS,oBAAqB,OACxDJ,KAAKK,MAGnB,C,kBAlBK,SAAAC,GAAA,KAG+BL,UAAW,OAI9BI,MACa,QAA5BE,EAAAA,EAAAA,SAAAA,I,gLAPUC,WAAW,K,uDAEXC,KAAMC,W"}

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,2 +0,0 @@
"use strict";(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([["12"],{5739:function(e,a,t){t.a(e,(async function(e,i){try{t.r(a),t.d(a,{HaNavigationSelector:()=>c});var d=t(73577),r=(t(71695),t(47021),t(57243)),n=t(50778),l=t(36522),o=t(63297),s=e([o]);o=(s.then?(await s)():s)[0];let u,h=e=>e,c=(0,d.Z)([(0,n.Mo)("ha-selector-navigation")],(function(e,a){return{F:class extends a{constructor(...a){super(...a),e(this)}},d:[{kind:"field",decorators:[(0,n.Cb)({attribute:!1})],key:"hass",value:void 0},{kind:"field",decorators:[(0,n.Cb)({attribute:!1})],key:"selector",value:void 0},{kind:"field",decorators:[(0,n.Cb)()],key:"value",value:void 0},{kind:"field",decorators:[(0,n.Cb)()],key:"label",value:void 0},{kind:"field",decorators:[(0,n.Cb)()],key:"helper",value:void 0},{kind:"field",decorators:[(0,n.Cb)({type:Boolean,reflect:!0})],key:"disabled",value(){return!1}},{kind:"field",decorators:[(0,n.Cb)({type:Boolean})],key:"required",value(){return!0}},{kind:"method",key:"render",value:function(){return(0,r.dy)(u||(u=h` <ha-navigation-picker .hass="${0}" .label="${0}" .value="${0}" .required="${0}" .disabled="${0}" .helper="${0}" @value-changed="${0}"></ha-navigation-picker> `),this.hass,this.label,this.value,this.required,this.disabled,this.helper,this._valueChanged)}},{kind:"method",key:"_valueChanged",value:function(e){(0,l.B)(this,"value-changed",{value:e.detail.value})}}]}}),r.oi);i()}catch(u){i(u)}}))}}]);
//# sourceMappingURL=12.ffa1bdc0a98802fa.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"12.ffa1bdc0a98802fa.js","sources":["https://raw.githubusercontent.com/home-assistant/frontend/20250401.0/src/components/ha-selector/ha-selector-navigation.ts"],"names":["HaNavigationSelector","_decorate","customElement","_initialize","_LitElement","F","constructor","args","d","kind","decorators","property","attribute","key","value","type","Boolean","reflect","html","_t","_","this","hass","label","required","disabled","helper","_valueChanged","ev","fireEvent","detail","LitElement"],"mappings":"mVAQaA,GAAoBC,EAAAA,EAAAA,GAAA,EADhCC,EAAAA,EAAAA,IAAc,4BAAyB,SAAAC,EAAAC,GAiCvC,OAAAC,EAjCD,cACiCD,EAAoBE,WAAAA,IAAAC,GAAA,SAAAA,GAAAJ,EAAA,QAApBK,EAAA,EAAAC,KAAA,QAAAC,WAAA,EAC9BC,EAAAA,EAAAA,IAAS,CAAEC,WAAW,KAAQC,IAAA,OAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAE9BC,EAAAA,EAAAA,IAAS,CAAEC,WAAW,KAAQC,IAAA,WAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAE9BC,EAAAA,EAAAA,OAAUE,IAAA,QAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,OAAUE,IAAA,QAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,OAAUE,IAAA,SAAAC,WAAA,IAAAL,KAAA,QAAAC,WAAA,EAEVC,EAAAA,EAAAA,IAAS,CAAEI,KAAMC,QAASC,SAAS,KAAOJ,IAAA,WAAAC,KAAAA,GAAA,OAAmB,CAAK,IAAAL,KAAA,QAAAC,WAAA,EAElEC,EAAAA,EAAAA,IAAS,CAAEI,KAAMC,WAAUH,IAAA,WAAAC,KAAAA,GAAA,OAAmB,CAAI,IAAAL,KAAA,SAAAI,IAAA,SAAAC,MAEnD,WACE,OAAOI,EAAAA,EAAAA,IAAIC,IAAAA,EAAAC,CAAA,mKAECC,KAAKC,KACJD,KAAKE,MACLF,KAAKP,MACFO,KAAKG,SACLH,KAAKI,SACPJ,KAAKK,OACEL,KAAKM,cAG5B,GAAC,CAAAlB,KAAA,SAAAI,IAAA,gBAAAC,MAED,SAAsBc,IACpBC,EAAAA,EAAAA,GAAUR,KAAM,gBAAiB,CAAEP,MAAOc,EAAGE,OAAOhB,OACtD,IAAC,GA/BuCiB,EAAAA,I"}

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,2 +0,0 @@
(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([["1236"],{4121:function(){Intl.PluralRules&&"function"==typeof Intl.PluralRules.__addLocaleData&&Intl.PluralRules.__addLocaleData({data:{categories:{cardinal:["one","other"],ordinal:["one","two","few","other"]},fn:function(e,n){var t=String(e).split("."),a=!t[1],l=Number(t[0])==e,o=l&&t[0].slice(-1),r=l&&t[0].slice(-2);return n?1==o&&11!=r?"one":2==o&&12!=r?"two":3==o&&13!=r?"few":"other":1==e&&a?"one":"other"}},locale:"en"})}}]);
//# sourceMappingURL=1236.64ca65d0ea4d76d4.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"1236.64ca65d0ea4d76d4.js","sources":["/unknown/node_modules/@formatjs/intl-pluralrules/locale-data/en.js"],"names":["Intl","PluralRules","__addLocaleData","n","ord","s","String","split","v0","t0","Number","n10","slice","n100"],"mappings":"wHAEIA,KAAKC,aAA2D,mBAArCD,KAAKC,YAAYC,iBAC9CF,KAAKC,YAAYC,gBAAgB,CAAC,KAAO,CAAC,WAAa,CAAC,SAAW,CAAC,MAAM,SAAS,QAAU,CAAC,MAAM,MAAM,MAAM,UAAU,GAAK,SAASC,EAAGC,GAC3I,IAAIC,EAAIC,OAAOH,GAAGI,MAAM,KAAMC,GAAMH,EAAE,GAAII,EAAKC,OAAOL,EAAE,KAAOF,EAAGQ,EAAMF,GAAMJ,EAAE,GAAGO,OAAO,GAAIC,EAAOJ,GAAMJ,EAAE,GAAGO,OAAO,GACvH,OAAIR,EAAmB,GAAPO,GAAoB,IAARE,EAAa,MAC9B,GAAPF,GAAoB,IAARE,EAAa,MAClB,GAAPF,GAAoB,IAARE,EAAa,MACzB,QACQ,GAALV,GAAUK,EAAK,MAAQ,OAChC,GAAG,OAAS,M"}

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 +0,0 @@
"use strict";(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([["1295"],{21393:function(s,n,e){e.r(n)}}]);

View File

@@ -0,0 +1,2 @@
"use strict";(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([["1303"],{29815:function(e,t,a){a.r(t),a.d(t,{HaFormConstant:function(){return h}});var s=a(73742),o=a(59048),n=a(7616);let r,l,i=e=>e;class h extends o.oi{render(){return(0,o.dy)(r||(r=i`<span class="label">${0}</span>${0}`),this.label,this.schema.value?`: ${this.schema.value}`:"")}}h.styles=(0,o.iv)(l||(l=i`:host{display:block}.label{font-weight:var(--ha-font-weight-medium)}`)),(0,s.__decorate)([(0,n.Cb)({attribute:!1})],h.prototype,"schema",void 0),(0,s.__decorate)([(0,n.Cb)()],h.prototype,"label",void 0),h=(0,s.__decorate)([(0,n.Mo)("ha-form-constant")],h)}}]);
//# sourceMappingURL=1303.586b15f0603e938e.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"1303.586b15f0603e938e.js","sources":["https://raw.githubusercontent.com/home-assistant/frontend/20250811.0/src/components/ha-form/ha-form-constant.ts"],"names":["HaFormConstant","LitElement","render","html","_t","_","this","label","schema","value","styles","css","_t2","attribute"],"mappings":"kPAMO,MAAMA,UAAuBC,EAAAA,GAKxBC,MAAAA,GACR,OAAOC,EAAAA,EAAAA,IAAIC,IAAAA,EAAAC,CAAA,uBAAuB,WAAvB,KAAuBC,KAAKC,MAAeD,KAAKE,OAAOC,MAC5D,KAAKH,KAAKE,OAAOC,QACjB,GACR,EATWT,EAWJU,QAASC,EAAAA,EAAAA,IAAGC,IAAAA,EAAAP,CAAA,0E,2BAVPQ,WAAW,K"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self.webpackChunkhome_assistant_frontend=self.webpackChunkhome_assistant_frontend||[]).push([["1374"],{5786:function(t,e,n){n.r(e),n.d(e,{HaIconNext:function(){return c}});n(26847),n(27530);var o=n(73742),s=n(7616),a=n(88479),r=n(993);class c extends r.HaSvgIcon{constructor(...t){super(...t),this.path="rtl"===a.E.document.dir?"M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z":"M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"}}(0,o.__decorate)([(0,s.Cb)()],c.prototype,"path",void 0),c=(0,o.__decorate)([(0,s.Mo)("ha-icon-next")],c)}}]);
//# sourceMappingURL=1374.3202a7d8086556de.js.map

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