Browse Source

home-assistant: address PR review

- home-assistant.container now gates on the live /config/configuration.yaml
  instead of the /etc/quadlets bootstrap template: once Home Assistant owns
  /config the bootstrap files are no longer needed.
- Move the init unit's inline shell into an external script,
  config/init.sh -> /etc/quadlets/home-assistant/init.sh, referenced from
  home-assistant-init.service.
- Drop the (non-essential) ConditionPathExists from home-assistant.target;
  its members self-gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat/home-assistant-cookbook
Nicolas Massé 2 days ago
parent
commit
cd75fca7f7
  1. 9
      cookbooks/home-assistant/README.md
  2. 41
      cookbooks/home-assistant/config/init.sh
  3. 34
      cookbooks/home-assistant/home-assistant-init.service
  4. 7
      cookbooks/home-assistant/home-assistant.container
  5. 2
      cookbooks/home-assistant/home-assistant.target

9
cookbooks/home-assistant/README.md

@ -40,9 +40,12 @@ templates).
| `/etc/quadlets/home-assistant/configuration.yaml` | Bootstrap Home Assistant configuration (copied to `/config`). | | `/etc/quadlets/home-assistant/configuration.yaml` | Bootstrap Home Assistant configuration (copied to `/config`). |
| `/etc/quadlets/home-assistant/secrets.yaml` | Bootstrap secrets, incl. the recorder database URL + password. | | `/etc/quadlets/home-assistant/secrets.yaml` | Bootstrap secrets, incl. the recorder database URL + password. |
Nothing starts until `/etc/quadlets/home-assistant/configuration.yaml` exists (the `home-assistant-init.service` only bootstraps `/config` when
`home-assistant.target`, `home-assistant.service` and `home-assistant-init.service` units `/etc/quadlets/home-assistant/configuration.yaml` is present; `home-assistant.service` only
all guard on it with `ConditionPathExists`). starts once `/config/configuration.yaml` exists (created by the init unit on first boot, or
already present if Home Assistant was configured before). So on a fresh system nothing runs
until the operator provides the bootstrap files, and once Home Assistant owns `/config` the
files under `/etc/quadlets/home-assistant/` are no longer required.
Ready-to-use examples are provided under `config/examples/`. They are installed to Ready-to-use examples are provided under `config/examples/`. They are installed to
`/etc/quadlets/home-assistant/` during development and testing only, and are **not** part `/etc/quadlets/home-assistant/` during development and testing only, and are **not** part

41
cookbooks/home-assistant/config/init.sh

@ -0,0 +1,41 @@
#!/bin/sh
#
# Bootstrap Home Assistant's /config on first start.
#
# Home Assistant owns /config once it has started: after first boot the live
# copies win and nothing here is touched again. This script:
# - copies the operator's bootstrap configuration.yaml / secrets.yaml from
# /etc/quadlets/home-assistant into /config only if they are absent;
# - creates the empty include targets configuration.yaml references
# (automations.yaml / scenes.yaml / scripts.yaml) so Home Assistant does not
# fall back to recovery mode on an otherwise-empty /config.
#
set -eu
SRC=/etc/quadlets/home-assistant
DST=/var/lib/virtiofs/data/home-assistant
HA_UID=10034
HA_GID=10000
install_if_absent() {
if [ -f "$SRC/$1" ] && [ ! -e "$DST/$1" ]; then
echo "Bootstrapping $DST/$1 from $SRC/$1"
install -m "$2" -o "$HA_UID" -g "$HA_GID" "$SRC/$1" "$DST/$1"
else
echo "Keeping existing $DST/$1 (Home Assistant owns it) or no bootstrap provided"
fi
}
stub_if_absent() {
if [ ! -e "$DST/$1" ]; then
echo "Creating empty $DST/$1"
echo "$2" > "$DST/$1"
chown "$HA_UID:$HA_GID" "$DST/$1"
fi
}
install_if_absent configuration.yaml 0644
install_if_absent secrets.yaml 0600
stub_if_absent automations.yaml "[]"
stub_if_absent scenes.yaml "[]"
stub_if_absent scripts.yaml "{}"

34
cookbooks/home-assistant/home-assistant-init.service

@ -15,36 +15,10 @@ Before=home-assistant.service
Type=oneshot Type=oneshot
RemainAfterExit=yes RemainAfterExit=yes
# Bootstrap /config ONLY where a file is absent. Home Assistant owns /config once # Bootstrap /config from the operator's templates (see init.sh for details). The
# it has started: after first boot the live copies win and nothing here is touched # script only ever creates files that are absent, so Home Assistant's live copies
# again. install_if_absent copies the operator's bootstrap files; stub_if_absent # are never overwritten.
# creates the empty include targets configuration.yaml references ExecStart=/etc/quadlets/home-assistant/init.sh
# (automations.yaml / scenes.yaml / scripts.yaml) so Home Assistant does not fall
# back to recovery mode on an otherwise-empty /config.
ExecStart=/bin/sh -c '\
set -eu; \
src=/etc/quadlets/home-assistant; \
dst=/var/lib/virtiofs/data/home-assistant; \
install_if_absent() { \
if [ -f "$src/$1" ] && [ ! -e "$dst/$1" ]; then \
echo "Bootstrapping $dst/$1 from $src/$1"; \
install -m "$2" -o 10034 -g 10000 "$src/$1" "$dst/$1"; \
else \
echo "Keeping existing $dst/$1 (Home Assistant owns it) or no bootstrap provided"; \
fi; \
}; \
stub_if_absent() { \
if [ ! -e "$dst/$1" ]; then \
echo "Creating empty $dst/$1"; \
echo "$2" > "$dst/$1"; \
chown 10034:10000 "$dst/$1"; \
fi; \
}; \
install_if_absent configuration.yaml 0644; \
install_if_absent secrets.yaml 0600; \
stub_if_absent automations.yaml "[]"; \
stub_if_absent scenes.yaml "[]"; \
stub_if_absent scripts.yaml "{}"'
[Install] [Install]
WantedBy=home-assistant.target WantedBy=home-assistant.target

7
cookbooks/home-assistant/home-assistant.container

@ -4,8 +4,11 @@ Documentation=https://www.home-assistant.io/
After=network.target var-lib-virtiofs-data.mount home-assistant-init.service After=network.target var-lib-virtiofs-data.mount home-assistant-init.service
Requires=var-lib-virtiofs-data.mount home-assistant-init.service Requires=var-lib-virtiofs-data.mount home-assistant-init.service
# Only start if Home Assistant has been configured # Only start once Home Assistant has been bootstrapped: the live configuration
ConditionPathExists=/etc/quadlets/home-assistant/configuration.yaml # lives in /config (populated by home-assistant-init.service on first boot, or
# already present if Home Assistant was configured before). Past that point the
# bootstrap files in /etc/quadlets are no longer needed.
ConditionPathExists=/var/lib/virtiofs/data/home-assistant/configuration.yaml
# Start/stop this unit when the target is started/stopped # Start/stop this unit when the target is started/stopped
PartOf=home-assistant.target PartOf=home-assistant.target

2
cookbooks/home-assistant/home-assistant.target

@ -6,8 +6,6 @@ After=postgresql.target home-assistant-init.service home-assistant.service
# Allow isolation - can stop/start this target independently # Allow isolation - can stop/start this target independently
AllowIsolate=yes AllowIsolate=yes
# Only start if Home Assistant has been configured
ConditionPathExists=/etc/quadlets/home-assistant/configuration.yaml
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

Loading…
Cancel
Save