Repository navigation
v0.57.0
Breaking Changes
Migration steps for the configuration items are in the upgrading guide. Run hassette run --check after upgrading.
- Hassette no longer exits when it can't reach Home Assistant — it reconnects in place and apps keep running against the last known state cache until HA is back. Apps that relied on the process restarting after an HA reboot to rerun
on_initializemust handlewebsocket_connected(orhomeassistant_started) instead. The[hassette.websocket]retry settings (connect_retry_*,early_drop_*,max_recovery_seconds) are removed and, since unknown keys now fail startup, must be deleted from your config.CouldNotFindHomeAssistantErrorand theTEST_EARLY_DROP_*testing constants are removed, andInvalidAuthErroris now aHassetteErrorrather than aFatalError. (#2743) - Unknown settings fail startup, and several config paths and names moved — see the groups below. (#2641)
- Unknown settings: an unknown
HASSETTE__*environment variable orhassette.tomlkey stops startup with exit code 78. The error names the key and suggests the closest real setting.HASSETTE__LOG_LEVELis nowHASSETTE__LOGGING__LOG_LEVEL. - Config locations:
config_dirdrives file lookup and can't be set inhassette.tomlor.env; pass--config-diror setHASSETTE__CONFIG_DIRin the process environment. Relative paths in a config file are relative to that file's directory, not the working directory. A config location you pass explicitly that doesn't exist is an error. Theconfig_fileandenv_filesettings are retired; use the--config-fileand--env-fileCLI flags. - App config: an
AppConfigenv_prefixthat starts withhassette__is now an unknown-key error because that namespace is reserved. Rename it to something likeMYAPP_.AppConfigreads the same.envfiles as the running Hassette config instead of the fixed/config/.env,./.envand./config/.envlist; setenv_filein the app'smodel_configto read a specific file. App config overrides throughHASSETTE__APPS__<APP_KEY>__CONFIG__<FIELD>keep working. - Exit codes and logging: a missing HA token and other config errors exit with code 78, so supervisors that restart on any non-zero exit should treat 78 as "edit the config first".
hassette runlogs to stdout; update log collection that read stderr. - Docker: apps moved from
/appsto/config/appsand the image no longer setsHASSETTE__APP_DIR. Move./appsto./config/appsand drop the./apps:/appsvolume, or keep the mount and setHASSETTE__APPS__DIRECTORY=/apps. Thedocker_start.shvariables were renamed with no fallback:HASSETTE__INSTALL_DEPS,HASSETTE__PROJECT_DIRandHASSETTE__PRUNE_UV_CACHEare nowHASSETTE_DOCKER_INSTALL_DEPS,HASSETTE_DOCKER_PROJECT_DIRandHASSETTE_DOCKER_PRUNE_UV_CACHE.
- Unknown settings: an unknown
run_app_precheckanddisable_state_proxy_pollingremoved — both now fail startup as unknown keys. The app precheck always runs; setallow_startup_if_app_precheck_fails = trueto start despite a failure. The state cache poll always runs; tune it withstate_proxy_poll_interval_seconds. (#2762)scheduler.min_delay_seconds,scheduler.max_delay_secondsandscheduler.default_delay_secondsremoved — they only tuned an internal sleep clamp that made jobs fire late, and have no replacement. Delete them fromhassette.tomlandHASSETTE__SCHEDULER__*environment variables, or startup fails with the unknown-key error above.behind_schedule_threshold_secondsis unchanged. (#2726)GET /api/logs/recentvalidatessource_tieras a case-sensitive enum —app | framework | all, defaulting toall, matching the telemetry endpoints. Values that used to be lowercased server-side, such assource_tier=APP, now return 422. (#2603)
For App Authors: You Can Now Delete
- A hand-rolled replay guard for
event.*entities after an HA restart — comparing the entity's timestamp state againstlast_changedor the clock inside a handler orshould_run(). Usewhere=P.EventEntityFresh(max_age=...). (#2631) changed_to=/changed_from=callables or numeric string literals written becauseTrueand20never matched — a lambda likelambda v: v == "on"for a boolean, or"20.0"for a number.changed_to=Truenow matches"on"andchanged_to=20matches"20.0". (#2684)- Reads of climate and water heater setpoints out of
attributes.extras(temperature,target_temp_high,target_temp_low,target_temp_step) — use the typedtarget_temperature,target_temperature_high,target_temperature_lowand, on water heaters,target_temperature_stepfields. (#2635) - Re-arming a one-shot job from its own callback through a different name or a deferred task —
run_in(..., name=<same name>, if_exists="replace")now works from inside the job's own callback. (#2650) - A supervisor restart policy kept only so Hassette comes back after a Home Assistant restart (Docker
restart:, a systemd unit, the add-on Watchdog) — Hassette now reconnects on its own. Keep the policy if you want it for crashes. (#2743)
Home Assistant Connection
- One reconnect loop now paces every failure. Refused connections, DNS failures and proxy 502/404 responses retry fast, with 1/2/4/5 s backoff plus jitter. A proxy's 401/403, a rejected certificate and a 429 (honoring
Retry-After) retry every 5 minutes. A rejected token is retried 3 times, 60 s apart, because HA's token check is sensitive to clock skew at boot, and then the connection stays inAUTH_FAILEDwithout reconnecting until Hassette restarts. (#2743) - A connection reports as connected only once HA's core state is
RUNNING, so apps never see a "connected" HA that is still booting. Events during the gap aren't replayed, and the state cache is replaced on reconnect. (#2743) - Outage logging is bounded at about 13 rows an hour: a WARNING on the first failure, a "still unreachable" summary every 5 minutes, INFO on recovery with the outage duration, and DEBUG for each attempt. Until the first successful connection, failures log at ERROR with a
base_urlhint. (#2743) - The WebSocket's
max_msg_sizeis raised to 16 MiB so a largeget_statespayload no longer triggers a close loop. (#2743) - A clean shutdown no longer logs
task ws:recv crashedat ERROR (#2759), andrequest_shutdown()logs at DEBUG instead of INFO once per app instance and service (#2758).
Bus
- Literal equality in predicates coerces Home Assistant's string state to the literal's type before comparing:
ValueIs,StateFrom/StateTo/AttrFrom/AttrTo, and thechanged_from=/changed_to=kwargs.C.Comparison("==", True)now agrees withchanged_to=True. Use the newC.Exact(value)when you need a strict match against the raw value. (#2684) P.EventEntityFresh(max_age=...)drops stale replays ofevent.*entities, such as buttons and remotes, that HA re-broadcasts after a restart. It compares the entity's timestamp state with itslast_changed, never reads the current clock, and fails open when either value is missing or unparseable. (#2631)wait_forcancellation warnings are rate-limited to one per 60 seconds per Bus outside shutdown, with a demoted count in the next warning. A WARNING now fires when pendingwait_forcalls reach 80% oflifecycle.max_concurrent_dispatches, andhassette statusandGET /api/healthreportpending_wait_for_count. (#2744, #2723, #2725)- Removing a listener through the sync facade (
bus.sync.remove_listener/remove_all_listeners) now cancels a pendingwait_foron the event-loop thread, so the waiter always wakes. (#2687)
Scheduler
- Jobs fire at their due time. The loop sleeps exactly until the next due job instead of flooring the sleep, which delayed jobs by up to 1 s in production and 2 s in dev. (#2726)
Afteracceptshours=, matchingEvery. Combiningtimedelta=withseconds=/minutes=/hours=now raisesValueError; before, the unit arguments were silently dropped. (#2648)- A
single/queued/restartjob that replaced itself from its own callback withif_exists="replace"used to hang forever. It now works for async and sync callbacks. (#2650) Scheduler.remove_all_jobs()no longer raisesTaskBucketSealedErrorduring a forced shutdown. The removal methods are typed asasyncio.Future[None]rather thanasyncio.Task; awaiting callers are unaffected. (#2660)- A handler or job that raises
TimeoutErroritself, such as await_fortiming out or anaiohttprequest timeout, is recorded as an error with a traceback instead oftimed_out.timed_outnow means the framework's execution deadline fired. (#2661)
State Models & Configuration
- Climate and water heater setpoints populate their typed fields from Home Assistant's wire keys (
temperature,target_temp_high,target_temp_low,target_temp_step). They were alwaysNonebefore, with the values only inattributes.extras. The values no longer appear inextras, so code reading them there must switch to the typed fields. (#2635) - A typed
AppConfigsubclass now warns when an app'shassette.tomlconfig has keys it doesn't declare, naming the app and suggesting the closest declared field. (#2585) display_nameset on an app inhassette.tomlis honored instead of silently dropped. (#2722)- Missing public types are exported from their package
__all__lists:TypedStateChangeEventandTypedStateChangePayloadfromhassette.eventsandhassette,FalseySentinelfromhassette.const,BlockingIOBehaviorandForgottenAwaitBehaviorfromhassette.types, andBlockingIODetectionConfigfromhassette.config.HassetteNotInitializedErrornow subclassesHassetteError, and still subclassesRuntimeError. (#2602)
Web UI & CLI
- The web UI can be served under Home Assistant ingress (
/api/hassio_ingress/<token>/, which the add-on's sidebar panel needs) and under sub-path reverse proxies such ashttps://example.com/hassette/. The base path comes fromX-Ingress-PathorX-Forwarded-Prefix, honored only from atrusted_proxiespeer with auth on. (#2720) - The connection indicator says "Can't reach server" and shows the URL when the WebSocket has never connected after five attempts. Reconnect delays are jittered so open tabs don't reconnect at the same moment. (#2760)
- The Diagnostics page refreshes system health when the HA connection drops or recovers. (#2614)
- Start and Reload are hidden for app instances removed from config, with a "not in config" chip on the instance. (#2653)
- A
start,stoporreloadrequest for an app that already has an action running gets an immediate 409action_in_progressinstead of queueing behind it. Re-pressing reload after a client timeout no longer starts a second full restart. (#2612) - Execution rows that can't open a detail view are skipped in keyboard tab order (#2643), and the sidebar's warn group is labeled WARNING instead of SLOW (#2637).
- Piped CLI tables stay greppable: with no TTY and no
$COLUMNS, tables render at 1000 columns instead of being ellipsized at 80.hassette log --instanceis hidden from--helpand completion. (#2645) hassette statusshowsapp_countas "N tracked instances", since it counts tracked app instances rather than configured apps. (#2629)
Bug Fixes
- Database size limit uses live data — the size failsafe could delete real telemetry rows from a database that was over
max_size_mbonly because of free pages or an untruncated WAL, andincremental_vacuumreclaimed one page per call. Delete decisions now use the size of in-use pages. After deletes, free pages are reclaimed in bounded chunks and the WAL is truncated, so the file on disk can sit above the limit by its free pages plus the WAL. The cache'sclear()runs a fullVACUUM. (#2751) - Retention and size-failsafe cleanup resume when
DatabaseServicerestarts instead of waiting out a full interval, and maintenance tasks configured shorter than the heartbeat run at their own interval. A retention cleanup that keeps skipping the cleanup of retired listeners and jobs escalates to ERROR after 3 consecutive cycles. (#2677, #2688, #2678) - Hassette shuts down cleanly, with the session finalized as a failure, when the web API can't bind its host and port. Before, the process ended with no teardown and the session row stayed
running. (#2719) - A WebSocket disconnect no longer records a cancelled
state_proxy_pollexecution. (#2623)
Documentation
- New guide for controlling Hassette apps from Home Assistant with the
hass-hassetteintegration. (#2625) - Docker dependencies page explains that project install and app loading are separate import paths, and shows the
src-layout build backend config and how to check the wheel withuv build --wheel. (#2683, #2691) - Troubleshooting page corrected: what the app precheck validates, the real class-not-found message, and how
forgotten_await_behavior = "error"behaves. (#2745) - Bus methods page defines
D,CandP, the acceptedchanged_from/changed_toforms and the shorthand methods' shared parameters. (#2721) - Blocking detection is described as event-loop stalls, CPU-bound work included, not only blocking I/O. The CLI reference explains
cancelledandtimed_outruns that makehassette job'sTotalexceedOK + Fail + Skipped. The lifecycle internals page states the correctServiceWatchershutdown order. (#2652, #2657, #2620)