feat(homeassistant): a scheduled OS update reboots the machine and is done once the new version runs (#6755)
CI / lint (push) Successful in 1s
CI / unit (push) Successful in 1m37s
CI / integration (push) Failing after 2m0s
CI / publish (push) Skipped

Home Assistant writes an OS update and leaves the reboot to someone, so a scheduled
one never finished unattended. The schedule now asks for the reboot once, follows it
through Home Assistant going away, and counts it done when the Supervisor reports a
different version with nothing pending. A machine that doesn't come back or falls
back to the old version fails after 20 minutes, which ends the slot and raises
update_failed. Install on the page and through MCP still leave the reboot to the person.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-01 19:15:03 -05:00
co-authored by Claude Opus 5.5
parent 4c57735dd9
commit ecc59cc882
10 changed files with 262 additions and 41 deletions
+5 -2
View File
@@ -63,12 +63,13 @@ class HomeAssistantUpdate(Base):
moves through backing_up and installing to done or failed, and its row
carries everything needed to carry on after Steward restarts. The *_before
columns are Home Assistant's own readings at the start, so "a newer backup"
is judged on its clock alone. One can be under way at a time.
is judged on its clock alone. One can be under way at a time. `reboot_after`
sends it through rebooting too, for an OS update the schedule installs.
"""
__tablename__ = "homeassistant_updates"
__table_args__ = (
Index("uq_homeassistant_update_under_way", text("(true)"), unique=True,
postgresql_where=text("state IN ('backing_up', 'installing')")),
postgresql_where=text("state IN ('backing_up', 'installing', 'rebooting')")),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
@@ -86,4 +87,6 @@ class HomeAssistantUpdate(Base):
backup_event_before: Mapped[str | None] = mapped_column(String(64), nullable=True)
backup_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
install_sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
reboot_after: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
reboot_sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)