Skip to content

v0.55.0

Choose a tag to compare

@nodejsmith-release-please nodejsmith-release-please released this 28 Sep 21:16
3ac4afe

Breaking Changes

  • TaskBucket.run_sync's timeout_seconds parameter now defaults to a NOT_PROVIDED sentinel instead of None. Previously, None meant "use the config default" and float("inf") meant "block forever" internally. Now, omitting the argument (or passing NOT_PROVIDED) means "use the config default," and None means "block forever" (matching concurrent.futures.Future.result(timeout=None)). run_sync is public, documented API — any caller that explicitly passed timeout_seconds=None to get the config default must omit the argument instead. (#2301)

Bus & API

  • New Bus.wait_for(topic, *, where=None, timeout, name=None) suspends until a matching event arrives and returns it, with no handler or subscription to manage. It only matches events dispatched after the call, which fits the "call a service, then wait for its effect" pattern. A sync-facade variant (self.sync.wait_for(...)) is also available. (#2301)
  • Topic is now exported from the top-level package: from hassette import Topic (#2360)
  • Api.call_service() gains wait_for_ack=True, which waits for Home Assistant to confirm the call and raises on failure. It works for services that return no response, where return_response=True would be rejected. (#2252)
  • Generated entity methods for services that return data now return the ServiceResponse instead of None: weather.get_forecasts(), todo.get_items(), media_player.browse_media(), and media_player.search_media() (#2237)

Telemetry & Database

  • New database.framework_retention_days (default 1) sets a separate, shorter retention window for framework-internal execution records. Previously they made up ~98% of the executions table and pushed app execution history out well before retention_days. Must be <= retention_days. After upgrading, the first cleanup removes framework records older than one day. (#2269)
  • Framework-tier executions are now recorded only when they are interesting. Errors and executions slower than 100 ms are always kept, and routine successes are dropped by default. Tune this with database.framework_record_errors, database.framework_record_slow_ms, and database.framework_record_sample_rate. Dropped records are counted in the new dropped_filtered field on /api/telemetry/status. App-tier executions are never filtered. (#2362)
  • The size failsafe now deletes framework executions first, then blocking events, then app executions, and it never deletes log records. Log expiry is controlled only by log_retention_days. (#2269, #2350)
  • Retention deletes now run in batches (database.retention_delete_batch, database.retention_max_batches_per_target), and a failure in one table no longer rolls back cleanup for the others (#2269)
  • New database.write_submit_timeout_seconds (default 60) bounds how long a telemetry write waits in the queue. A long retention pass now delays live telemetry instead of stalling it until the queue overflows. (#2377)
  • The database.heartbeat_interval_seconds, retention_interval_seconds, size_failsafe_*, and max_consecutive_heartbeat_failures settings are now honored. Previously they were accepted but ignored in favor of hardcoded values. (#2296)

Logging

  • New logging.extra_loggers setting sends loggers outside the hassette.* namespace (for example, your app's own my_app.notify logger or a third-party library) through Hassette's formatting, log capture, and database persistence (#1949)
  • Connecting to, reconnecting to, and disconnecting from Home Assistant are now logged at INFO with the URL, so a working token is visible at the default log level (#1959)
  • The effective log_persistence_level is now clamped to log_level, so the dashboard never hides records that are actively being logged (#2411)
  • web_api.log_buffer_size has been removed. Existing configs that set it still load. (#2411)

Web UI

  • The live log view now refreshes from the database instead of assembling entries from the WebSocket stream, so live rows carry the same execution/handler links as the initial page load. The live view shows the most recent 1000 records. (#2411)
  • The sidebar shows the app key instead of the display name when two apps share a display name (#2243)

CLI

  • A 401 from hassette status and other commands now says which credential was sent and where it came from, and lists the remedies (--token-file, cli.token_file, HASSETTE__CLI__AUTH_TOKEN). This also covers the confusing case where a second local instance's token is rejected. (#1960)
  • hassette job shows a Skipped column, so a predicate-gated job whose predicate never passes no longer reads as Total 68 / OK 0 / Fail 0 (#1958)

Packaging

  • hassette now depends on two new packages released in lockstep with it, hassette-wire and hassette-client. Both are currently empty placeholders for an upcoming split of framework internals. No imports change. (#2422)
  • The Docker image ships httpx2/httpcore2 2.12.0, clearing five published CVEs (#2151)

Bug Fixes

  • App failures no longer reach framework supervision. A failing app no longer produces spurious ServiceWatcher skipping restart warnings or risks a whole-process shutdown, and a crashing app no longer marks the session as failed. (#1955, #2324)
  • Starting an app that is already running no longer replaces its live instances and orphans their listeners, jobs, and tasks (#2245)
  • An instance left running after its app's configured instance count shrinks can now be stopped from the API and the web UI (#1962)
  • POST /api/apps/{app_key}/start and /reload now return 500 with the error message when the instance ends up FAILED, instead of reporting success with 202 (#2370)
  • HelperClient.increment(), decrement(), and reset() for counters no longer fail every time with service_validation_error (#2252)
  • Queued invocations dropped during teardown no longer permanently shrink a listener's available dispatch concurrency (#1957)
  • Reading state while the state cache is unavailable (for example, in an on_shutdown hook) now raises immediately instead of blocking the event loop for ~0.3–0.4 s in a retry that could never succeed (#1956)
  • Hot-reloading apps no longer accumulates duplicate logging filters, which caused slowly growing memory use and slower logging (#2247)
  • A stalled database write worker is now detected and restarted instead of leaving the service reporting healthy while telemetry silently stops (#1953)
  • Retention and size-failsafe cleanups that could not be queued are retried on the next heartbeat instead of being skipped for a full interval (#2351)
  • Sessions that recorded a crash but never shut down cleanly are now closed out on the next start, so their once=True listeners get cleaned up (#2246)
  • cleanup(timeout=0) on apps and resources now means "don't wait" instead of silently using the default shutdown timeout. Comparing a job that was never scheduled no longer raises AttributeError. (#2251)
  • predicate_description in listener and job telemetry (REST API, CLI --json, UI) shows a readable qualified name for bare-callable predicates instead of a bound-method repr with a memory address (#1961)
  • The Docker entrypoint no longer leaves a copy of the mounted project in /tmp/project-build.* on every container start (#2347)
  • Fixed missing web UI styles: secondary and muted text rendered at the wrong size and color, and dialog, drawer, popover, and tooltip animations were not running (#2379)
  • The Code tab's line-number gutter no longer shows an extra, empty final line (#2275)
  • Screen readers no longer announce the decorative icon on empty-state views (#2202)

Documentation

  • The first-automation guide now explains async handlers, placeholder entities, and the difference between handlers and scheduled jobs (#2412)
  • The testing docs define their harness terms, use valid MotionLights configs throughout, consolidate the seed-vs-simulate guidance into one place, and document that freeze_time takes a process-global lock (#2407, #2415)
  • Fixed the bus filtering "Custom Accessors" example, which type-checked but never matched at runtime (#2374)
  • Every predicate's summarize() now documents what it matches and the shape of the summary it returns (#2404)