Commit Graph
5747 Commits
Author SHA1 Message Date
dependabot[bot] 637f3cdd4e Bump coverage from 7.14.1 to 7.14.2 (#6968)
Bumps [coverage](https://github.com/coveragepy/coveragepy) from 7.14.1 to 7.14.2.
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.14.1...7.14.2)

---
updated-dependencies:
- dependency-name: coverage
  dependency-version: 7.14.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>
2026-06-22 09:10:12 +02:00
dependabot[bot] d6dbb283f9 Bump deepmerge from 2.0 to 2.1.0 (#6967)
Bumps [deepmerge](https://github.com/toumorokoshi/deepmerge) from 2.0 to 2.1.0.
- [Release notes](https://github.com/toumorokoshi/deepmerge/releases)
- [Commits](https://github.com/toumorokoshi/deepmerge/compare/v2.0...v2.1.0)

---
updated-dependencies:
- dependency-name: deepmerge
  dependency-version: 2.1.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>
2026-06-22 09:10:01 +02:00
dependabot[bot] 3788e5952f Bump pytest from 9.1.0 to 9.1.1 (#6966)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.1.0 to 9.1.1.
- [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/9.1.0...9.1.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.1.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>
2026-06-22 09:02:16 +02:00
Franck Nijhof cd08532c2f Use a set for backup app slug lookups when removing delta apps (#6962)
remove_delta_apps checked membership against the app_list property for
every installed app. That property rebuilds a list on each access, so
the check rebuilt and linearly scanned it once per installed app.
Resolve the slugs into a set once before the loop for constant-time
membership checks. Behavior is unchanged.
2026-06-19 14:06:06 +02:00
Franck Nijhof cb4023569e Precompute bind mount paths in folder backup filter (#6956)
The per-file exclusion filter in folder backups looped over
sys_mounts.bound_mounts for every file. That property rebuilds a list
from a dict on each call, so backing up a folder with many files meant
rebuilding the list and scanning it linearly once per file.

Bind mounts don't change during a backup, so resolve the set of paths to
skip once up front and turn the per-file check into a single set
membership test.
2026-06-19 09:51:04 +02:00
Franck Nijhof 3c9af57d26 Allow slashes in add-on repository branch names (#6957)
Add-on repositories can pin a branch with the "url#branch" syntax. The
branch part of the repository regex only accepted word characters,
hyphens and dots, so a branch with a slash like "feature/hot-new-stuff"
failed to match and the repository was rejected as invalid.

Git branch names commonly use prefixes such as "feature/" or "fix/", so
allow slashes in the captured branch name.
2026-06-19 09:45:00 +02:00
dependabot[bot] 5b52ea7a4c Bump actions/checkout from 6.0.3 to 7.0.0 (#6959)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 08:41:34 +02:00
dependabot[bot] 04a03996d7 Bump ruff from 0.15.17 to 0.15.18 (#6960)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 08:41:18 +02:00
Stefan AgnerandClaude Opus 4.8 1857753e22 Redact app options in info for unprivileged apps (#6953)
The `/addons/{slug}/info` endpoint returned the target app's user options,
which can contain secrets such as passwords and API keys. The security
middleware grants every role (including the default role) access to any
`/.+/info` path, so an installed app with `hassio_api: true` and the default
role could read another app's options simply by requesting its info.

Redact the options field in info_data() unless the caller is entitled to see
it: Home Assistant Core (and other non-app internals), the app reading its
own info, or an app with the manager or admin role. Other apps reading a
different app's info now receive an empty options dict while all non-secret
metadata stays available for discovery. This mirrors the existing self-only
restriction on the dedicated /options/config endpoint.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:54:57 +02:00
Stefan AgnerandClaude Opus 4.7 95bb8fe6ab Make Core.shutdown idempotent and safe to call concurrently (#6891)
* Make Core.shutdown idempotent and safe to call concurrently

After #6887, Core.shutdown() now runs in the SIGTERM path during host
shutdown in addition to the existing host.control reboot/shutdown and
backup restore paths. Multiple concurrent callers were possible (e.g.
SIGTERM arriving while a reboot API call is mid-flight), so __main__.py
debounced the signal handler by stashing the in-flight task in a single-
element list and bailing out on the second SIGTERM.

Move the idempotency into Core.shutdown() itself, where it belongs:

- A second call while shutdown is in progress awaits the in-flight
  shutdown via an asyncio.Event rather than re-running the sequence.
- Calls during STOPPING/CLOSE return early (Supervisor is already going
  away; the work is moot).
- Calls during STARTING_STATES (INITIALIZE/STARTUP/SETUP) return early
  too. There is nothing coherent to gracefully stop before startup
  completes, and on the SIGTERM-during-startup path the caller cancels
  startup_task first, so waiting for it to complete would deadlock.
- The sequence is wrapped in try/finally so the completion event is set
  even when an inner step raises.

With that in place the closure workaround in __main__.py collapses to a
plain coresys.create_task(stop_supervisor()): repeat SIGTERMs spawn
extra tasks but each just observes the in-flight shutdown and waits.

Tests cover the four state branches and confirm the event is reset
between repeated shutdown cycles (backup restore re-enters RUNNING).

* Split Core.shutdown() into teardown_services + shutdown

PR feedback (@mdegat01) flagged the "supports repeated use" comment on
_shutdown_event.clear() as describing a use case that does not exist.
Investigating, the real source of confusion is that the old shutdown()
did two different things stitched together:

  - Stop user-facing containers (add-ons + Home Assistant Core), which
    backup restore uses while leaving the host alone.
  - Run the full shutdown ceremony (state transition to SHUTDOWN, stop
    plugins), which only the SIGTERM signal handler and the host
    reboot/power-off API want.

That dispatch was implemented with an asymmetric state transition
("only set SHUTDOWN if state == RUNNING") and a plugin-shutdown gate
("only stop plugins if state in (STOPPING, SHUTDOWN)"). It worked but
made the intent of each branch hard to read, broke the reentrancy
guard on the restore path (state never reaches SHUTDOWN, so concurrent
callers fall through every early return), and forced the misleading
"repeated cycles" framing on the event handling.

Split into two methods with one job each:

  - teardown_services(): stop add-ons + Home Assistant Core. Does not
    change Core state and does not stop plugins. Backup restore calls
    this directly so HA Core's watchdog stays registered (it only
    disables on transitions into CLOSING_STATES) and plugins keep
    running for the restore body to use.
  - shutdown(): real shutdown ceremony. Unconditionally transitions to
    SHUTDOWN, calls teardown_services(), then stops plugins. The
    reentrancy guard (state == SHUTDOWN -> await event) now works
    correctly because every caller transitions state on entry. One-shot
    per process lifetime; no clear() needed.

Update backups/manager.py:867 to call teardown_services() instead.
remove_homeassistant_container moves to teardown_services() since
restore is the only caller that passes it.

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

* Release shutdown waiters when set_state() is cancelled

PR feedback from Copilot: set_state() updates Core._state synchronously
(line 84 in core.py) before awaiting _write_run_state(). If the
shutdown task is cancelled while awaiting that write, in-memory state
is already SHUTDOWN but the function exits before entering the
try/finally that sets _shutdown_event. Any concurrent or later
shutdown() caller then sees state == SHUTDOWN and blocks forever on
_shutdown_event.wait().

Move the set_state(SHUTDOWN) call inside the try so finally always
runs and releases waiters. CancelledError still propagates to the
caller after finally as expected; we just no longer leak the lock.

Add a regression test that simulates cancellation inside
_write_run_state() and asserts both that state has moved to SHUTDOWN
and that _shutdown_event is set.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-17 17:21:03 +02:00
dependabot[bot] 5110f78edf Bump sentry-sdk from 2.62.0 to 2.63.0 (#6952)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.62.0 to 2.63.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.62.0...2.63.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.63.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>
2026-06-17 10:23:03 +02:00
dependabot[bot] dcb31f3f9f Bump release-drafter/release-drafter from 7.3.1 to 7.4.0 (#6951)
Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 7.3.1 to 7.4.0.
- [Release notes](https://github.com/release-drafter/release-drafter/releases)
- [Commits](https://github.com/release-drafter/release-drafter/compare/693d20e7c1ce1a81d3a41962f85914253b518449...ed4bc48ec97379be2258e7b7ac2624a3e26ab809)

---
updated-dependencies:
- dependency-name: release-drafter/release-drafter
  dependency-version: 7.4.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>
2026-06-17 10:22:11 +02:00
Stefan AgnerandClaude Opus 4.8 81e235376e Fix typos repo-wide and add codespell pre-commit hook (#6949)
* Fix typos in comments, docstrings and log messages

Correct 39 spelling mistakes across comments, docstrings and log/error
message strings throughout the package (e.g. "conection" -> "connection",
"Incomming" -> "Incoming", "Rasie" -> "Raise"). All changes are confined
to human-readable text; no identifiers, attributes or D-Bus contracts are
touched, so there is no behavior change. Found with codespell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix typos in tests and CI workflow

Correct spelling mistakes in test comments, docstrings and data, plus one
in the builder workflow, so the whole tree is clean for the codespell hook
added next. The assertion in test_network_manager.py is updated to match
the corrected "Unknown error while processing" log message in the source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add codespell pre-commit hook

Wire up codespell so spelling mistakes in comments, docstrings and strings
are caught automatically. The vendored frontend panel is excluded, and
"hass" and "astroid" are added to the ignore list as known false positives
(the Home Assistant abbreviation and the pylint dependency package).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Address review feedback

Improve grammar in several of the touched comments and docstrings: use the
plural "ignore conditions" for the list-returning property, add the missing
auxiliary verb and fix agreement in the timezone-filter comment, fix
"backups ... use" agreement, and reword "underlay" to "underlying" in the
arch module docstring.

Also drop the "*.json" skip from the codespell hook. It was carried over
from another project but is unnecessary here (all tracked JSON is clean),
and skipping it would needlessly leave translation and data JSON unchecked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Reword onboarding comment

"overflight" was a literal calque of the German "überflogen"; use the
idiomatic "skimmed through" instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026.06.2
2026-06-16 14:09:16 +02:00
Stefan AgnerandClaude Opus 4.8 dae48c62e4 Treat mount errors as API errors instead of unexpected failures (#6946)
Mount failures generally reflect user configuration or host conditions
(unreachable server, wrong credentials, ...) rather than a Supervisor
bug. As a plain HassioError, MountError reached the generic error branch
of api_process, which logged a full traceback as an "Unexpected error
during API call" and captured the exception to Sentry. The mount reload
path already encoded the opposite intent by explicitly skipping Sentry
for MountError.

Make MountError an APIError so mount failures are surfaced as client-side
errors with their explicit message, without a traceback or Sentry noise,
matching the existing JobException handling. MountNotFound additionally
inherits from APINotFound so it returns a 404 instead of a 400.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 09:32:22 +02:00
dependabot[bot] 4e52a50941 Bump home-assistant/builder from 2026.03.2 to 2026.06.0 (#6947)
Bumps [home-assistant/builder](https://github.com/home-assistant/builder) from 2026.03.2 to 2026.06.0.
- [Release notes](https://github.com/home-assistant/builder/releases)
- [Commits](https://github.com/home-assistant/builder/compare/62a1597b84b3461abad9816d9cd92862a2b542c3...4de35182ce1e329181bffcbcc84d33db5e2c7e10)

---
updated-dependencies:
- dependency-name: home-assistant/builder
  dependency-version: 2026.06.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>
2026-06-16 09:32:11 +02:00
dc6a77507b fix(docker): restore add-on device access after USB re-enumeration (#6877)
* fix(docker): register hw listener and match by-id paths for options-based devices

Two bugs caused a crash loop when a USB device re-enumerates to a different
minor number (e.g. ttyACM0→ttyACM1) after a HAOS reboot:

1. _hw_listener was only registered when addon.static_devices was non-empty.
   Addons that expose a device via the options schema (e.g. Z-Wave JS `device:`
   option) never had the listener registered, so add_devices_allowed was never
   called when the device reappeared at a new minor.

2. _hardware_events matched only device.path and device.sysfs against
   static_devices.  When static_devices (or the new options path) contains a
   by-id symlink, the match always failed because by-id paths live in
   device.links.

Fix: extend the listener registration condition to also cover addon.devices
(options-based), and expand the path-matching set to include device.links so
by-id paths resolve correctly.  For options-based devices, compare the incoming
Device against addon.devices (which re-evaluates options.json against the live
hardware list, picking up the new minor number automatically).

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

* fix(docker): use option_device_paths for cheap by-id hw event matching

Refactor _hardware_events to avoid per-event full options validation
(including pwnd hashing). Introduce AppOptions.extract_device_paths and
AppModel.option_device_paths to extract raw device paths from options
without resolving against live hardware. Use set-intersection against
{device.path, device.sysfs, *device.links} so by-id symlinks match
correctly after re-enumeration for both static and options-based devices.

Update test to use real schema/options setup and simulate a minor-number
change (ttyACM0→ttyACM1) with a stable by-id symlink.

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

* test: improve hw listener test coverage and add policy check

Address PR review feedback:
- Add hardware policy check in _hardware_events to prevent bypassing access
  restrictions on hotplug events (follows same pattern as startup cgroup setup)
- Fix test_app_options_device_hw_listener to properly simulate USB re-enumeration
  with different minor numbers (166:0 → 166:1)
- Add test_app_options_device_policy_check to verify policy enforcement for
  options-based devices
- Update TEST_HW_DEVICE with realistic major/minor attributes (166:0 for tty)

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

* test: mock HostFeature.OS_AGENT in hardware event tests

The _hardware_events method has a @Job decorator with conditions=[JobCondition.OS_AGENT],
which checks if HostFeature.OS_AGENT is in sys_host.features. Without mocking this,
the job conditions fail and the hardware event handler is never invoked, causing
add_devices_allowed to not be called and tests to fail.

Add patch.object(type(coresys.host), "features", ...) to all four hardware event tests
to ensure the OS_AGENT job condition is met.

Fixes test failures:
- test_app_new_device (all 6 parametrized cases)
- test_app_new_device_no_haos
- test_app_options_device_hw_listener
- test_app_options_device_policy_check

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

* test: fix TEST_DEV_PATH to match TEST_HW_DEVICE.path

TEST_DEV_PATH was set to /dev/ttyAMA0 but TEST_HW_DEVICE.path is /dev/ttyACM0.
This mismatch would cause the dev_path=TEST_DEV_PATH parametrized test cases
to fail because the hardware event handler checks if the device path intersects
with the app's allowed devices, and "/dev/ttyAMA0" != "/dev/ttyACM0".

Update TEST_DEV_PATH from /dev/ttyAMA0 to /dev/ttyACM0 to match TEST_HW_DEVICE.

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

* test: mock option_device_paths in test_app_options_device_hw_listener

The test sets up schema and options but option_device_paths property may not
be working as expected in the test environment. Add an explicit mock for
option_device_paths to ensure it returns the by-id path, guaranteeing that:
1. The hardware listener is registered (checks option_device_paths at registration)
2. The device path matching works correctly in _hardware_events

This ensures the test properly validates that hardware events are processed
for options-based devices after re-enumeration.

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

* test: make policy check test actually exercise the policy guard

test_app_options_device_policy_check set the device option via
persist["options"], but option_device_paths reads the merged options and
does not pick that override up during the test, so it returned an empty
set. The hardware event therefore failed the path-match guard and returned
before reaching the allowed_for_access check. The assert_not_called()
assertion then passed regardless of the policy outcome -- it would still
pass if the policy guard were removed entirely.

Mock option_device_paths to return the configured by-id path (mirroring
test_app_options_device_hw_listener) so the event device matches and
execution actually reaches the policy guard the test is meant to verify.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: add unit test for AppOptions.extract_device_paths

The integration tests exercise extract_device_paths only through a mocked
option_device_paths property, so the schema-walking logic introduced for
the hardware-event matching had no direct coverage.

Add a unit test that drives every schema shape the recursion handles --
flat, optional, filtered, list, nested dict and list of dicts -- and
asserts that non-device options, unset keys and empty values are skipped,
without requiring the devices to exist in hardware.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
2026-06-15 22:49:49 +02:00
Stefan AgnerandClaude Opus 4.8 41908284b4 Fix typo in APIResolution class name (#6944)
The REST API handler class for the resolution center was misspelled as
"APIResoulution", which also leaked into its docstring and the related
comment in core.py. Rename it to "APIResolution" and correct the
surrounding spelling. The class is internal to the Supervisor package,
so no public API path is affected.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 21:24:05 +02:00
Jan Čermák ecf890a41c Fix blacklist for v2 endpoints in security middleware, add v2 security tests (#6933)
The blacklist in the security middleware didn't take the v2 prefix into
account, allowing to call routes that are supposed to be blacklisted for
apps with hassio and homeassistant APIs enabled in app config.

Add test checking that these routes are always blacklisted, and
parametrize other tests using v2 endpoints in the test_security module.
2026-06-15 17:55:58 +02:00
Stefan AgnerandClaude Opus 4.8 153108754c Don't offer a rebuild repair for a detached app (#6932)
This is a corner case: a locally-built app whose container image went missing
for some external reason and which is also detached from its store, e.g. its
local source folder was removed. There is no normal Supervisor command that
produces this combination -- rebuild removes the image before rebuilding, but
it is rejected for detached apps, so it cannot leave a detached app without an
image. The image loss here came from outside Supervisor's normal flow.

Such an app cannot be rebuilt: there is no source. App.load still surfaced a
MISSING_IMAGE repair with an EXECUTE_REPAIR suggestion for it, and the
resolution autofix loop then tried to rebuild it. That dead-ends in
App.path_location, which raises for a detached app, crashing the autofix with
an unhandled exception.

Don't create the rebuild repair when the app is detached. The detached-app
check already surfaces a DETACHED_ADDON_REMOVED issue with an EXECUTE_REMOVE
suggestion for these apps (the built-in local repository is always loaded), so
the user is offered removal instead of a repair that can never succeed.

Also guard the repair fixup itself against a detached build app. With the
App.load change this is only reachable via a race: the repair is created while
the app is attached and the app detaches before the autofix runs (or between
its retries). Skip gracefully there too rather than throwing, mirroring the
is_detached guards already in AppManager.update/rebuild.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:55:40 +02:00
Jan Čermák dbb1634bbd Fix ha os import in OS 18.0.rc1 and newer (#6943)
The hassos-config service was renamed to haos-config in
home-assistant/operating-system#4740. This change preserved the old name
as an alias, however, Supervisor uses ListUnits that doesn't report the
aliases and then thinks such unit doesn't exist. To fix that, simply
call the unit by its primary name which now varies by the OS version.

Fixes #6942
2026-06-15 17:34:41 +02:00
Stefan AgnerandClaude Opus 4.8 e775546960 Warn when skipping persistent timezone on OS older than 16.2 (#6945)
Persisting the timezone to the host requires Home Assistant OS 16.2 or
newer, since /etc/localtime is not writable on older versions. While 16.2
was still unreleased this was logged at info level, as running an older OS
was expected. Now that 16.2 has shipped, an OS that still cannot persist
the timezone is outdated, so raise the message to a warning to make the
limitation visible to the user. Drop the now-resolved TODO and its fixme
suppression.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 17:34:26 +02:00
Jan Čermák 7870cd786f Detect landingpage by io.hass.type label instead of version (#6935)
The landingpage image now stamps a real Core version into its
io.hass.version label rather than the sentinel "landingpage" string.
Supervisor decided whether Core was still just a landingpage by
comparing that version against the LANDINGPAGE constant, so with the
new label it mistook the landingpage for an installed Core. Restarting
the Supervisor mid-install then stranded the system on the landingpage
until the next OS reboot, with no install job scheduled and the
watchdog hot-looping on the missing Core auth API.

Override the version resolution for the Home Assistant container so a
landingpage image (io.hass.type == "landingpage") always reports
LANDINGPAGE, keeping every existing version check working unchanged
while leaving io.hass.version free to carry the real version.

Fixes #6934
2026-06-15 17:22:16 +02:00
dependabot[bot] 998b79f3aa Bump cryptography from 48.0.1 to 49.0.0 (#6941)
Bumps [cryptography](https://github.com/pyca/cryptography) from 48.0.1 to 49.0.0.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/48.0.1...49.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 49.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>
2026-06-15 09:36:21 +02:00
dependabot[bot] beaad3be01 Bump pylint from 4.0.5 to 4.0.6 (#6940)
Bumps [pylint](https://github.com/pylint-dev/pylint) from 4.0.5 to 4.0.6.
- [Release notes](https://github.com/pylint-dev/pylint/releases)
- [Commits](https://github.com/pylint-dev/pylint/compare/v4.0.5...v4.0.6)

---
updated-dependencies:
- dependency-name: pylint
  dependency-version: 4.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>
2026-06-15 09:35:51 +02:00
dependabot[bot] 81bdadacf2 Bump pytest from 9.0.3 to 9.1.0 (#6939)
Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.3 to 9.1.0.
- [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/9.0.3...9.1.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.1.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>
2026-06-15 09:35:37 +02:00
dependabot[bot] 9df7655e60 Bump ruff from 0.15.16 to 0.15.17 (#6936)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.16 to 0.15.17.
- [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.15.16...0.15.17)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.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>
2026-06-12 11:22:14 +02:00
dependabot[bot] 03d1a98b19 Bump cryptography from 48.0.0 to 48.0.1 (#6931)
Bumps [cryptography](https://github.com/pyca/cryptography) from 48.0.0 to 48.0.1.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/48.0.0...48.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 48.0.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>
2026-06-10 13:27:00 +02:00
Stefan AgnerandClaude Opus 4.8 7eaf69fcab Derive installed-app source location from the store (#6929)
* Derive installed-app source location from the store

The installed app's source location was persisted as an absolute string in
apps.json, captured at install time. The addons->apps directory migration
renamed the source directories but left that stored string pointing at the old
addons path, so locally-built apps failed to build with a misleading
"dockerfile is missing" error on install/update/rebuild (#6917).

The location is not really app state: it is the source directory discovered
during the store scan and recomputed on every store reload. Derive it from the
store data (App.path_location -> data_store) instead of persisting it, and drop
ATTR_LOCATION from the system schema. REMOVE_EXTRA strips the stale value from
existing apps.json files, so no migration is needed and already-migrated
instances are fixed as well.

location only exists for a store-backed app. The store is loaded before apps
during setup (Core.setup ordering), so an installed, attached app can always
resolve it; reaching path_location while detached is a programming error and
now raises. Detached apps have no source, so their asset accessors
(with_icon/logo/changelog/documentation, long_description) report absence
rather than reading a path, and the path cache is no longer refreshed for them.
Build, apparmor install and backup/restore never touch path_location on a
detached app (build/update are blocked when detached; apparmor and the built
image are captured from the host and restored from the backup), so those paths
are unaffected.

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

* Guard path_location on app_store instead of is_detached

Address review feedback: path_location guarded on is_detached while the asset
accessors guarded on app_store, two ways of expressing the same condition.
Key path_location off app_store as well, matching install()/update() and the
store API, so the check is consistent and is_detached is no longer needed here.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026.06.1
2026-06-09 17:23:03 +02:00
Jan Čermák 446a1aacd9 Return 404 for raspberrypi endpoints on boards without firmware (#6926)
* Return 404 for raspberrypi endpoints on boards without firmware

Return "not found" when board doesn't have the firmware update
available. Any other error may indicate it's worth retrying later (as
discussed in [1]), so 404 is more appropriate here.

[1] https://github.com/home-assistant/core/pull/172929#discussion_r3363706261

* Consistently return APINotFound with debug log
2026-06-09 17:15:19 +02:00
Stefan AgnerandClaude Opus 4.8 3de0de05fb Report progress during initial Core install (#6904)
* Sync docker pull progress to Core install job

The home_assistant_core_install job had no child_job_syncs, so the
download/extract progress tracked on the internal docker_interface_install
job never propagated to the user-visible install job. On a fresh system,
/jobs reporting therefore jumped from 0 to 100 with nothing in between
during the initial Core install.

Mirror what home_assistant_core_update already does and sync the docker
pull progress up to the install job, allocating it the full progress
range. As with the update path, this treats the image pull as 100% of the
task even though image cleanup and Home Assistant start are not accounted
for.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Test Core install job is exposed with progress for landing page

The landing page frontend (home-assistant/frontend#52359) polls /jobs/info
during the initial Core install and reads the progress of a root job named
home_assistant_core_install to render a download progress bar. Add a test
that guards this contract: the install must expose a non-internal
home_assistant_core_install job whose progress is driven by the docker image
pull, reporting intermediate values rather than a bare 0 to 100 jump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Exclude Supervisor self-update pull from Core install progress

The home_assistant_core_install (and _update) jobs sync their progress from
the docker image pull via child_job_syncs. The install job may first trigger
a Supervisor self-update, which also pulls an image through a
docker_interface_install job. With an unscoped filter that pull incorrectly
counted towards Core install progress, driving the user-visible job to 100%
before the actual Core image download even started.

Scope the child sync to the Home Assistant container reference so only the
Core image pull contributes. Promote the container name constant to public
(HASS_DOCKER_NAME) so it can be reused for the filter reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Reset synced progress when a child job is re-triggered

A parent job syncs its progress from a child via child_job_syncs, capturing
the parent's progress at the time the child starts as the band the child
fills. When the same child ran more than once - e.g. the Core image pull,
which runs in a retry loop and which Docker resumes at layer granularity -
the second child stacked on top of the first attempt's leftover progress,
pushing the user-visible job straight to 100% instead of restarting.

Record a per-sync baseline on the parent: the first child to match a sync
captures the baseline, and children matching the same sync afterwards reuse
it and reset the parent back to it. This implements the behavior the prior
HACK comment anticipated (reset progress instead of skipping the second
sync) and removes the now-unneeded progress >= 100 skip.

Add tests covering an install retry (progress resets rather than overshoots)
and a Supervisor self-update done first (its pull does not count towards the
Core install job).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 12:33:03 +02:00
dependabot[bot] f3f108f549 Bump sentry-sdk from 2.61.1 to 2.62.0 (#6930)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.61.1 to 2.62.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.61.1...2.62.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.62.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>
2026-06-09 12:28:43 +02:00
1e5d7812cd Bump aiohttp from 3.14.0 to 3.14.1 (#6922)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Stefan Agner <stefan@agner.ch>
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-08 11:44:30 -05:00
Stefan Agner d910efa49e Surface Home Assistant update failures as translatable API errors (#6915)
Updating Core to a non-existent version (e.g. a mistyped beta tag) reported
success on the CLI: the image pull failed with a 404, but update() swallowed
the error via "with suppress(HomeAssistantError)" and then ran its post-update
health check against the still-running old Core, which passed.

Split the update routine into image install and start phases. A failed image
install leaves the running Core untouched, so there is nothing to health-check
or roll back; let it bubble out instead of masking it. Only failures after the
image is in place (e.g. the new container starting unhealthy) fall through to
the health check and rollback logic, where the error is now captured at debug
level rather than silently suppressed.

Model the update errors as client-facing APIError subclasses carrying an
error_key so the frontend can translate them and they no longer reach Sentry as
unexpected errors:

- HomeAssistantUpdateError: generic update failure
- HomeAssistantUpdateImageError: image download failed (includes the version)
- HomeAssistantUpdateAlreadyInstalledError: requested version already installed
2026-06-08 15:39:23 +02:00
dependabot[bot] 0ec650bac2 Bump pytest-aiohttp from 1.1.0 to 1.1.1 (#6924)
Bumps [pytest-aiohttp](https://github.com/aio-libs/pytest-aiohttp) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/aio-libs/pytest-aiohttp/releases)
- [Changelog](https://github.com/aio-libs/pytest-aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/pytest-aiohttp/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: pytest-aiohttp
  dependency-version: 1.1.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>
2026-06-08 12:07:19 +02:00
dependabot[bot] 4c5a8543ca Bump codecov/codecov-action from 6.0.1 to 7.0.0 (#6921)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6.0.1 to 7.0.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/e79a6962e0d4c0c17b229090214935d2e33f8354...fb8b3582c8e4def4969c97caa2f19720cb33a72f)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  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>
2026-06-08 12:05:51 +02:00
dependabot[bot] 0dc3880483 Bump dbus-fast from 5.0.19 to 5.0.22 (#6923)
Bumps [dbus-fast](https://github.com/bluetooth-devices/dbus-fast) from 5.0.19 to 5.0.22.
- [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/v5.0.19...v5.0.22)

---
updated-dependencies:
- dependency-name: dbus-fast
  dependency-version: 5.0.22
  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>
2026-06-08 11:38:16 +02:00
dependabot[bot] 99ac55e352 Bump home-assistant/wheels from 2025.12.0 to 2026.06.0 (#6920)
Bumps [home-assistant/wheels](https://github.com/home-assistant/wheels) from 2025.12.0 to 2026.06.0.
- [Release notes](https://github.com/home-assistant/wheels/releases)
- [Commits](https://github.com/home-assistant/wheels/compare/e5742a69d69f0e274e2689c998900c7d19652c21...34957438948e0b3dcde73c77750643dadae594f5)

---
updated-dependencies:
- dependency-name: home-assistant/wheels
  dependency-version: 2026.06.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>
2026-06-08 10:50:57 +02:00
Stefan AgnerandClaude Opus 4.8 5ea7908184 Drop obsolete UNIX_SOCKET_CORE_API feature flag (#6908)
Unix socket communication between Supervisor and Home Assistant Core was
initially gated behind the UNIX_SOCKET_CORE_API feature flag, then
enabled by default for Core 2026.5.1+ while older supported versions
still required the flag. Now that the transport has settled and is on by
default, the flag no longer serves a purpose.

Remove the feature flag and the CORE_UNIX_SOCKET_DEFAULT_VERSION split,
collapsing supports_unix_socket back to a single check: the Unix socket
is used for any non-landingpage Core version at or above
CORE_UNIX_SOCKET_MIN_VERSION.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 09:52:30 +02:00
Stefan AgnerandClaude Opus 4.8 8ec1c33aa4 Fix WebSocket transport None race condition in proxy (#6241)
Add a transport validity check before the WebSocket upgrade to handle
clients that disconnect during the handshake.

The connection can be lost between the Home Assistant API state check and
the server.prepare() call, leaving request.transport as None. aiohttp's
_pre_start() then raises ConnectionResetError (an AssertionError prior to
aiohttp 3.14.0), which propagates out of the handler as an unhandled
exception. The result is a 500 response and a Sentry report for what is
really just a client disconnect.

The fix detects the closed connection early and raises HTTPBadRequest
with a clear reason, turning the race into a clean 4xx response with a
warning log instead of error noise.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 09:48:47 +02:00
dependabot[bot] e525d22b06 Bump dbus-fast from 5.0.17 to 5.0.19 (#6913)
Bumps [dbus-fast](https://github.com/bluetooth-devices/dbus-fast) from 5.0.17 to 5.0.19.
- [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/v5.0.17...v5.0.19)

---
updated-dependencies:
- dependency-name: dbus-fast
  dependency-version: 5.0.19
  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>
2026-06-05 09:03:35 +02:00
dependabot[bot] ab44007e46 Bump ruff from 0.15.15 to 0.15.16 (#6914)
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.15.15 to 0.15.16.
- [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.15.15...0.15.16)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.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>
2026-06-05 09:00:09 +02:00
dependabot[bot] 5bb73bf32a Bump actions/checkout from 6.0.2 to 6.0.3 (#6911)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [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/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.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>
2026-06-04 14:33:13 +02:00
dependabot[bot] cf62a104e9 Bump getsentry/action-release from 3.6.1 to 3.7.0 (#6910)
Bumps [getsentry/action-release](https://github.com/getsentry/action-release) from 3.6.1 to 3.7.0.
- [Release notes](https://github.com/getsentry/action-release/releases)
- [Changelog](https://github.com/getsentry/action-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/action-release/compare/f71adb49d4b2aeeda98052d3de234bbb0f3e03ab...ff07929a6537bac57790c3451cf4d364aca38528)

---
updated-dependencies:
- dependency-name: getsentry/action-release
  dependency-version: 3.7.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>
2026-06-04 14:01:13 +02:00
c2b5482b22 Bump aiohttp from 3.13.5 to 3.14.0 (#6902)
* Bump aiohttp from 3.13.5 to 3.14.0

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

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

* Adjust API layer for aiohttp 3.14

aiohttp 3.14 raises NotAppKeyWarning when a plain string is used as a
request storage key. Convert REQUEST_FROM to a web.RequestKey instance so
request[REQUEST_FROM] no longer triggers the warning (which the test suite
escalates to an error, failing every authenticated endpoint). It is typed
as RequestKey[Any] to preserve the current access semantics: handlers store
different origins (App, Home Assistant, host, observer) and narrow the value
to the concrete type they expect.

aiohttp 3.14 also widened the request.post() return type to include
bytearray. Update the _process_dict annotation accordingly to satisfy mypy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Use encode_basic_auth for registry token requests

aiohttp 3.14 deprecates the BasicAuth constructor and the client auth=
request parameter (both removed in aiohttp 4.0), each emitting a
DeprecationWarning at runtime. The registry manifest fetcher hit both when
requesting a token with stored credentials. Switch to aiohttp.encode_basic_auth()
and pass the result via an Authorization header instead.

Add a test covering the credentials path, which the existing tests skipped by
mocking _get_auth_token. It asserts the Authorization header is sent and, since
the suite escalates warnings to errors, guards against the deprecations
returning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

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>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026.06.0
2026-06-03 10:09:57 +02:00
2f331aafa9 Add Raspberry Pi firmware update API (#6886)
* Add Raspberry Pi firmware update API

Expose `io.hass.os.Boards.RaspberryPi.Firmware` via a D-Bus proxy and
`GET/POST /os/boards/raspberrypi/firmware[/update]` REST endpoints for
Raspberry Pi 4 / 5 / CM4 (Yellow). Gated on OS Agent >= 1.9.0. The
update job raises a `REBOOT_REQUIRED` resolution issue on success and
rejects up front when the agent reports `update_blocked`.

The `blocked_reason` field currently returns only
`unsupported_boot_device` regardless of the underlying cause (CM4
without self-update, USB/NVMe boot, etc.), more reasons may be added
later if we need to make distinction.

Refs home-assistant/operating-system#4631

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix pylint issue in tests

* Return blocked_reason=None instead of empty string when update is not blocked

* Fix typo in update_raspberrypi_firmware docstring

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

* Reject API call for update early if blocked

* Flip API availablility check in _check_rpi_firmware_available

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

* Remove extra newline in docstring

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-02 17:37:25 +02:00
dependabot[bot] ee610c1f03 Bump debugpy from 1.8.20 to 1.8.21 (#6901)
Bumps [debugpy](https://github.com/microsoft/debugpy) from 1.8.20 to 1.8.21.
- [Release notes](https://github.com/microsoft/debugpy/releases)
- [Commits](https://github.com/microsoft/debugpy/compare/v1.8.20...v1.8.21)

---
updated-dependencies:
- dependency-name: debugpy
  dependency-version: 1.8.21
  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>
2026-06-02 13:47:23 +02:00
dependabot[bot] 226100322f Bump dbus-fast from 5.0.16 to 5.0.17 (#6900)
Bumps [dbus-fast](https://github.com/bluetooth-devices/dbus-fast) from 5.0.16 to 5.0.17.
- [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/v5.0.16...v5.0.17)

---
updated-dependencies:
- dependency-name: dbus-fast
  dependency-version: 5.0.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>
2026-06-02 13:35:19 +02:00
Stefan AgnerandClaude Opus 4.8 9f553c327c Only force a versioned Supervisor update in DEV mode (#6903)
Home Assistant Core now triggers a versionless Supervisor update during
onboarding to ensure Supervisor is current before it continues setup. It
treats a "no update available" response as the signal to proceed.

In DEV mode the update endpoint bypassed the need_update check entirely
and resolved a versionless request to the latest published version. So
Core's onboarding call made a freshly-built DEV Supervisor install the
latest published build and restart, which breaks the run_supervisor CI
job (the API disappears mid-test with "connection refused").

Tie the bypass to an explicit version instead: specifying a version is
still DEV-only, but a versionless request now always respects
need_update. Since need_update is always False in DEV, Core's onboarding
call becomes a no-op there, avoiding the update to the latest published
Supervisor on the dev channel.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 13:26:22 +02:00
dependabot[bot] 8926c7287e Bump sentry-sdk from 2.61.0 to 2.61.1 (#6898)
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.61.0 to 2.61.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.61.0...2.61.1)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-version: 2.61.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>
2026-06-01 22:25:04 +02:00
Stefan AgnerandClaude Opus 4.8 a973d22e35 Derive App state from container state (#6890)
* Derive App state from container state

The App.state setter mixed two responsibilities: it both mutated a
private `_state` field and dispatched side effects (WebSocket events,
issue dismissal, startup_event signaling). On top of that, an installed
but never-started app stayed in AppState.UNKNOWN forever, because the
attach() image-only fallback never fires a container state-change event
and the AppState therefore kept its constructor default. Conceptually,
ContainerState.UNKNOWN ("container does not exist") and AppState.UNKNOWN
("nothing observed yet") happened to share a name but meant different
things, which made the distinction easy to lose.

Make App.state a pure derived property. The source of truth is the last
observed ContainerState (cached on the App), plus a sticky operation-
error flag for start/stop failures that the docker event stream cannot
reflect. When no container has been observed yet, the derivation falls
back to install signals: an attached instance (image present) is
STOPPED, otherwise UNKNOWN. As a side effect, an installed-but-never-
started app now correctly reports STOPPED instead of UNKNOWN.

container_state_changed updates the cached container state and routes
all side effects through a single _emit_state_change helper that diffs
old vs new derived state. The two start/stop failure paths route
through _set_operation_error. Uninstall resets the cached signals so
the derivation naturally returns UNKNOWN.

Tests use a new tests/common.force_app_state helper that pokes the
underlying signals directly; the production class no longer carries
test-only setters.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Fix App state drive to AppState.UNKNOWN

* Unify state mutation through _update_state

Previously, state-driving signal changes were spread across two helpers
(_set_operation_error, _emit_state_change(old_state)) and required each
caller to capture self.state before mutating a private field — leaking
implementation details to call sites and raising the "why am I emitting
the old state?" question pointed out in code review.

Replace both helpers with a single _update_state(*, container_state=,
operation_error=) entry point. Callers describe what changed via
keyword arguments (None leaves a signal untouched); the helper captures
the previous state, applies the updates, recomputes the derived state
and emits side effects if anything changed.

Diff against a tracked _last_state instead of a freshly derived
"current" state, so that an out-of-band mutation between updates does
not silently shift the comparison baseline. The concrete case is
App.uninstall: instance.remove() clears the docker meta mid-flow, which
would otherwise reshape the derivation (RUNNING with no healthcheck
becomes STARTED instead of STARTUP) and suppress the STARTUP transition
that resolves the start-wait task. As a side effect, the initial
UNKNOWN -> STOPPED transition on attach is also now reliably emitted.

Switch the uninstall path to ContainerState.UNKNOWN ("we know there is
no container") rather than the constructor sentinel None.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Cache app state instead of deriving on every read

Building on the previous commit, make App.state a plain read of a
cached _state field rather than re-deriving on every property access.
The derivation moves to _derive_state(), and _update_state() is the
sole place that recomputes and assigns _state, so the value consumers
read always matches what was last emitted to listeners.

This removes the _last_state bookkeeping introduced previously: with a
single cached value there is no longer a separate "derived now" vs
"last emitted" distinction to reconcile, and out-of-band mutations
(e.g. instance.remove() clearing _meta during uninstall) can no longer
silently shift what state returns between updates.

Call _update_state() at the end of load() so the cached state settles
once attach() has run. Image-only attaches do not fire a docker event,
so without this an installed app would stay in the constructor-default
UNKNOWN until first start; this also makes the initial transition on
attach observable to listeners.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Pass operation error to _derive_state instead of storing it

The two state-driving signals were not symmetric. _container_state is
genuinely persisted state ("the last thing docker told us") that
re-derivation legitimately reads across calls. _operation_error, on the
other hand, is a momentary "force ERROR for this transition" signal; the
persistence of an error condition already lives in the cached _state.

Storing it as an instance attribute implied a sticky cross-call behavior
that no call path actually exercised: every caller either set it
explicitly right before deriving (start/stop failures, container events)
or ran argless only at load time, where no failure has occurred.

Drop the _operation_error field and pass operation_error as a parameter
to _derive_state(), defaulting to False in _update_state(). A container
observation now supersedes a prior error implicitly via the default,
which lets the container-event and uninstall call sites drop their
explicit operation_error=False.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Settle load state synchronously from current_state

The argless _update_state() settle at the end of load() raced attach()'s
container-state event. attach() fires DOCKER_CONTAINER_STATE_CHANGE via
the bus, which schedules the container_state_changed listener as a task
rather than running it inline. In the deprecated-arch early-return path
there is no await between attach() and the settle, so the listener had
not run yet: _container_state was still None and the settle derived
STOPPED (instance attached) — emitting a transient UNKNOWN->STOPPED even
for a running container before the listener corrected it. The main path
only avoided this incidentally, by having awaits (check_image,
save_persist) in between for the listener to run.

Derive the load-time state synchronously from instance.current_state()
instead of relying on the asynchronously delivered event. current_state()
returns the real container state, or UNKNOWN when only an image is
present (which derives to STOPPED), so both paths settle correctly
without racing the event.

Add a regression test that loading a running container settles to
STARTED, and mock current_state() in the state-listener test which
relies on a clean UNKNOWN baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:50:06 +02:00