Skip to content

Releases: stewart-php/hass-stewart

v1.0.0

Choose a tag to compare

@stewart-repository-split stewart-repository-split released this 13 Oct 17:53

The first stable release. From here on, stewart-php/contracts, the generated classes, configuration keys,
environment variables, console commands and the image entrypoint settings change only in a major release. The code is
the same as 0.11.1.

Added

Changed

  • The skeleton uses the runtime:1 image tag, and make upgrade writes the major for image tags

Upgrading

  1. Run make upgrade VERSION=1.0. Protocols are unchanged, so a 0.11 daemon, stewart CLI and Home Assistant
    integration work with 1.0.
  2. Change the image tag in Dockerfile and the compose files from runtime:1.0 to runtime:1 (1-dev in
    compose.dev.yaml), so the image keeps getting PHP and Alpine fixes after 1.1. Copy bin/upgrade-stewart.sh from
    the skeleton so later upgrades do the same.

v0.11.1

Choose a tag to compare

@stewart-repository-split stewart-repository-split released this 13 Oct 12:43

Publishes the runtime image, Helm chart and GitHub release that 0.11.0 did not.

Fixed

  • The release scans each runtime image for its own platform, so the arm64 build no longer fails it

v0.11.0

Choose a tag to compare

@stewart-repository-split stewart-repository-split released this 13 Oct 12:30

Handlers can wait for states and events, apps can define their own Home Assistant actions, and AppHarness makes
apps easy to test. Workers can be restarted on demand, Stewart can run from environment variables alone, and many
reliability issues are fixed.

Added

  • Awaitable waits with timeouts: waitForState(), EventStream::waitForNext() and Timers::waitFor()
  • App-defined Home Assistant actions with EntityExposure::exposeAction()
  • AppHarness and ManualScheduler in stewart-php/testing for testing apps on virtual time
  • HaContext::wasCausedByStewart() to tell Stewart's own changes apart
  • stewart worker:restart and POST /api/workers/{id}/restart
  • Running without a stewart.yaml, configured from the environment alone
  • App options read from a file through STEWART_APPS__<APP>__OPTIONS__<NAME>_FILE
  • StateChange::withFrom()

Changed

  • whenChangedTo() takes from: and fromAnyState: arguments instead of chained calls
  • stewart-php/contracts holds only the app-facing API; internal helpers moved to stewart-php/support
  • stewart-php/testing is public and covered by the backward-compatibility check
  • Exceptions are created with createForReason(), and collections with fromList(); most shortcut factories are gone
  • A full subscription queue drops superseded changes first, so each entity's latest change still arrives
  • History queries share the service-call in-flight limit
  • off or an empty value disables persistence.url, mqtt.url and the listen settings
  • App IDs in stewart.yaml may use _ in place of -
  • Control and IPC protocol 29, component protocol 2 and generated code format 6

Fixed

  • Large resyncs and bursts of state changes are no longer partly lost
  • Exposed entities come back right after the stewart integration reloads
  • Commands to a paused or stopped app are refused at once instead of timing out
  • Environment overrides work for hyphenated app IDs and camelCase options
  • One-shot timers that come due while an app is paused run when it resumes
  • Startup stops when no worker can start, and readiness reports when no worker is running
  • Handlers that keep failing from timers log far less
  • MQTT no longer loops on invalid topics, unacknowledged messages or rejected connections
  • Release pipeline: package tags, CI gate and image scanning

Removed

  • StateTransitionStream, ->from() and ->fromAnyState()
  • Injectable StewartIdentity
  • runAtSunrise() and runAtSunset()

Upgrading

  1. Run make upgrade VERSION=0.11, then make generate. Upgrade the daemon, the stewart CLI and the Home Assistant
    integration together; older versions refuse to talk to 0.11.
  2. Replace ->whenChangedTo('on')->from('off') with ->whenChangedTo('on', from: 'off'), and ->fromAnyState()
    with fromAnyState: true.
  3. Replace runAtSunrise() and runAtSunset() with runAtSunEvent(SunEvent::Sunrise, …) or SunEvent::Sunset.
  4. Replace an injected StewartIdentity with $ha->wasCausedByStewart($change).
  5. Replace removed exception factories with createForReason(), fromWire() with restoreFromWire(), and removed
    collection factories such as fromIds() with fromList().
  6. Import classes that moved out of stewart-php/contracts from Stewart\Support\….
  7. Custom implementations of Timers, EventStream, HaContext or EntityExposure add the new methods.
  8. An app option variable ending in _FILE is now read as a file; set the option in stewart.yaml to keep passing
    the path itself.

v0.10.0

Choose a tag to compare

@stewart-repository-split stewart-repository-split released this 12 Oct 10:01

A maintenance release: quieter worker shutdown, tighter package manifests and a hardened skeleton CI.

Fixed

  • Workers stopping during daemon shutdown no longer log errors: a worker in the new Stopping state drops sends after
    its shutdown request, and a disconnecting worker counts as closed
  • Generated field summaries keep a label or description of "0"

Changed

  • stewart-php/contracts and stewart-php/runtime require ext-filter; stewart-php/client and
    stewart-php/store-redis no longer require revolt/event-loop directly
  • RecordingLogger and RecordedLog in stewart-php/testing accept any log context array, as PSR-3 does
  • The skeleton's workflows check out without persisted credentials and grant packages: write only to the image jobs;
    its Dockerfile sets WORKDIR /app in the build stage

Upgrading

  1. Run make upgrade VERSION=0.10. Control and IPC protocols are unchanged, but broker, workers and stewart
    commands should all run 0.10.
  2. Update the stewart integration to 0.10.0 in Home Assistant.
  3. Optionally take over the skeleton's .github/workflows/*.yml and Dockerfile changes.

v0.9.0

Choose a tag to compare

@stewart-repository-split stewart-repository-split released this 09 Oct 21:19

Apps now show up in Home Assistant as entities of their own. The new stewart integration, installed through HACS,
lets an app expose sensors, switches, buttons, numbers, selects and other controls, receive their commands, and edit
entity registry entries.

Highlights

  • The stewart Home Assistant integration. Installed from stewart-php/hass-stewart through HACS; the daemon
    detects it on every connect and stewart status shows it on a component row.
  • Exposed entities. An injected EntityExposure gives apps sensor, binary_sensor, switch, button,
    number, select, text, time, date and datetime entities. watchCommands() streams typed commands, and
    entities turn unavailable while their app or daemon is down.
  • Registry edits. An injected RegistryEditor renames, labels, hides or moves entities in Home Assistant's entity
    registry.
  • Testing. RecordingEntityExposure and RecordingRegistryEditor in stewart-php/testing cover both in unit
    tests.

Added

  • The protocol between Stewart and its Home Assistant integration, integrations/home-assistant/PROTOCOL.md, with
    JSON goldens for every message in integrations/home-assistant/tests/protocol/
  • The stewart Home Assistant integration, installed through HACS from stewart-php/hass-stewart or by unpacking
    stewart.zip from its releases; Home Assistant 2026.4 or newer. It has a config flow, stewart/version and
    stewart/session/subscribe
  • The stewart integration creates and drives sensor and binary_sensor entities through
    stewart/entity/upsert, state and remove. Each daemon gets a hub device and each app a device under it, or an
    entity names its own device. Entities restore their state and attributes after a Home Assistant restart and become
    unavailable while their daemon has no session
  • stewart/entity/reconcile removes every entity of a daemon that it no longer exposes, except those of the apps it
    names in keep_apps
  • make ha-check (ruff, mypy, pytest; HA=min for Home Assistant 2026.4) as part of make check, and
    make ha-hassfest
  • The daemon detects the stewart integration on every Home Assistant connect and holds its session open;
    stewart status shows it on a component row, with the reason when it is missing, speaks another protocol or was
    taken over by another daemon
  • expose.instance setting (default), naming this daemon in Home Assistant; daemons sharing one Home Assistant need
    different names
  • Shortly after startup, once every worker is ready or quarantined, the daemon removes its entities that no running
    app exposed; entities of apps that failed or did not start stay. expose.prune: false turns it off
  • Metrics stewart_component_info{version,protocol} and stewart_component_state{state}
  • stewart status shows how many entities each app exposes in an exposed column, also exported as
    stewart_app_exposed_entities
  • Apps expose their own sensor and binary_sensor entities through an injected EntityExposure:
    exposeSensor() and exposeBinarySensor() return handles that set the value, attributes and availability and
    read back the entity ID and the state Home Assistant restored. Exposed entities are sent again after every
    reconnect; without a compatible integration, expose*() throws ExposureException
  • Stewart\Testing\Exposure\RecordingEntityExposure for unit tests of apps that expose entities
  • Stewart\Testing\Registry\RecordingRegistryEditor records registry edits and applies them to an InMemoryRegistry
  • The skeleton's HelloApp exposes sensor.stewart_hello_changes_seen; without the integration it logs why and keeps
    running
  • make test-ha-e2e (HA=min|latest) boots a real Home Assistant with the integration and checks that the skeleton's
    sensor appears, updates and turns unavailable when the daemon stops; CI runs it on both versions
  • The stewart integration creates switch and button entities. Their service calls send a command event, with
    the call's context, on the session and wait for stewart/command/result; a refusal, a timeout or the session
    ending fails the call
  • Apps expose switches and buttons with exposeSwitch() and exposeButton(). watchCommands() streams
    SwitchCommand and ButtonPress; a handler that returns accepts the command, CommandException::rejected()
    fails it with its message. Commands for paused or stopped apps fail at once, and a worker dying mid-command fails
    it without waiting for the timeout
  • expose.command_timeout setting (10s, at most 300s): how long Home Assistant waits for an app's answer
  • Metric stewart_app_exposed_commands_total{outcome} (accepted, rejected, refused, lost)
  • RecordingExposedSwitch::pushCommand() and RecordingExposedButton::pushCommand() run an app's command handlers in
    unit tests
  • The skeleton's HelloApp exposes switch.stewart_hello_counting, which pauses its change counter, and
    number.stewart_hello_step, how much each change adds, whose icon shows the pause through updateConfig(); the
    e2e smoke test sets the step and turns counting off and on
  • The stewart integration creates number, select, text, time, date and datetime entities; their
    set_value and select_option service calls send set_value and select_option commands
  • Apps expose numbers, selects, texts, times, dates and datetimes with exposeNumber(), exposeSelect(),
    exposeText(), exposeTime(), exposeDate() and exposeDateTime(). Times are TimeOfDay, dates and datetimes
    DateTimeImmutable. watchCommands() streams a typed command per platform; an accepted command sets the handle's
    value. The RecordingExposed* handles push them in unit tests
  • Every exposed handle has getConfig() and updateConfig(), which changes the entity's name, icon, options or
    limits in place and keeps its device; Home Assistant drops a value that no longer fits to unknown
  • RegisteredEntity carries the entity's icon, and aliases as EntityAlias where Home Assistant sends them; the
    mirrored registry has none, so it holds null there
  • Apps edit Home Assistant entity registry entries through an injected RegistryEditor: updateEntity() takes an
    EntityRegistryUpdate that renames, sets the icon, area, labels and aliases, hides or disables an entity, and
    returns the updated RegisteredEntity. Added and removed labels and aliases merge with the entry Home Assistant
    holds. Edits share the service-call budget and follow service_calls.dry_run; failures throw
    RegistryEditException. Editing an entity that a Stewart app exposes logs a warning naming the app and key

Changed

  • Entities of an app that fails, stops or loses its worker turn unavailable until the app exposes them again, instead
    of keeping their last state as if live
  • The control protocol is version 28: snapshots carry component and each app's exposed_entities and
    exposed_commands
  • IPC protocol 26; broker and workers must run the same version

Upgrading

  1. Run make upgrade VERSION=0.9. Broker, workers and stewart commands must all run 0.9; stewart status against
    a 0.8 daemon is refused (control protocol 28).
  2. Install the stewart integration 0.9.0 in Home Assistant 2026.4 or newer before an app calls expose*(); without
    it, expose*() throws ExposureException. Apps that expose nothing need no integration.
  3. Daemons sharing one Home Assistant set a different expose.instance each.