Martin Bauer 5763df90f8 Pin each MusicMouse host to a deploy branch, and freeze musicmouse
musicmouse_version pointed both hosts at rearchitect-backend, a branch that only
ever existed on a laptop - so the previous commit's rework could never actually
have run. Point each host at a deploy/<host> branch on the remote instead. Those
branches are the deployment record: what is installed on that box right now,
moved with a force-push when you mean to deploy.

musicmouse (Kinderzimmer) is the reason this matters. It is in daily use and
still runs the pre-rearchitecture backend out of /opt/musicmouse_venv and
espmusicmouse/host_driver/main.py. That commit has no python-backend/
pyproject.toml, no python-backend/musicmouse.service and no web/ at all, so this
role cannot install it - and a run would have force-checked-out incompatible
code over a working device first and failed afterwards. Set
mediapi_install_kidsmusic: false there and leave it alone;
deploy/musicmouse records the commit that is installed, for rollback. Add a
stat + fail right after the checkout so the same mistake stops with an
explanation instead of failing three tasks deeper.

Also in the role:

- Install the typing game's curriculum next to config.yml. A config with a
  tippen: section refuses to start without it - curriculum_file is validated as
  must-exist - so musicdolphin's new config needs it present.

- Stop clobbering config.yml. The app writes to it at runtime: parent mode
  patches the volume keys and the remote-control page rewrites the remote:
  block. A plain copy: undid that on every run. Install-once by default, with
  -e pi_musicmouse_force_config=true to push a change deliberately.

- Fill out config-musicdolphin.yml against the current schema: library root and
  cache, an absolute static_dir (../web/dist would resolve to /media/web/dist
  from /media/musicmouse and silently serve nothing), tippen, and the volume
  range for a screen-driven instance. ha: is left commented out until the
  Wohnzimmer-oben entity ids are picked - the ones in the dev config are
  Arbeitszimmer. No mqtt: (it would publish a device of dead entities on a
  simulate-only host) and no lirc:/remote: (this host runs irserver, a serial
  daemon, not the lircd this speaks TCP to).

The curriculum is a verbatim copy of app content, so exclude it from
ansible-lint rather than reformat it away from its source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 19:04:57 +02:00
2024-07-28 08:45:01 +02:00
2026-09-19 16:28:00 +02:00
2026-09-10 21:33:19 +02:00
2026-09-19 16:28:00 +02:00
2026-09-10 21:33:19 +02:00

ansible

Personal Ansible setup for provisioning and maintaining a fleet of Raspberry Pis (audio players, sensors, music mouse, etc.) plus one home server.

Layout

  • inventory.yml — hosts and group vars (Pis under iot, plus server). group_vars/ and host_vars/ hold vars for the mediapis group (the four parallel audio-player Pis) and their host-specific overrides.
  • mediapis.yml, working.yml, server.yml, newrpi-provisioning.yml, octopisetup.yml — top-level playbooks. mediapis.yml is the canonical playbook for the mediapis group (musicdolphin, kitchenpi, bedroompi, musicmouse); the others are narrower/ad-hoc runs kept around for specific hosts or one-off tasks.
  • roles/ — one role per piece of functionality (audio backends, sensors, bluetooth monitoring, server basics, etc.). Each has a short README.md.
  • update-packages.yml — deliberate, fleet-wide package update (see "Keeping packages up to date" below). update-packages-pinned-example.yml is a template for pinning or bumping a single package outside that.
  • lookup_plugins/keepass.py — custom lookup plugin that fetches secrets (device passwords, wifi passphrase) from a running KeePassXC instance via its browser-integration protocol, instead of storing them in the repo.
  • pis/ — loose config files/scripts used when provisioning Pis by hand.
  • scripts/ — standalone helper scripts (Raspbian image creation, a network logger) that aren't Ansible roles.
  • archive/ — retired setups kept for reference (not actively maintained).

Setup

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Secrets are pulled from KeePassXC at run time via the keepass lookup plugin — see the header of lookup_plugins/keepass.py for how to enable Browser Integration in KeePassXC. Freshly-flashed Raspberry Pis are reached first with the OS-default pi/raspberry credentials (see roles/pi_standard_setup), which the role then rotates to a KeePassXC-managed password.

Running a playbook

ansible-playbook mediapis.yml --limit <host>

ansible.cfg points Ansible at inventory.yml and roles/ by default, so no extra flags are needed for those.

Keeping packages up to date

Regular playbook runs (mediapis.yml, server.yml, etc.) use state: present for packages, so they only install what's missing — they never upgrade anything as a side effect of an unrelated config change. Two separate, deliberate mechanisms handle upgrades instead:

Security patches — automatic. The unattended_upgrades role (applied to every host in mediapis.yml/server.yml) configures unattended-upgrades to install security-origin updates automatically, with a scheduled reboot window (default 03:00, see roles/unattended_upgrades/defaults/main.yml) for patches that need one. Not scoped to full dist-upgrades.

Everything else — deliberate, ad hoc.

just update <host_or_group>          # e.g. `just update kitchenpi` or `just update`
venv/bin/ansible-playbook update-packages.yml --limit <host> --check --diff   # dry run first

Defaults to upgrade: safe (upgrades in place, never installs/removes packages to resolve dependencies — see the comment header in update-packages.yml for the tradeoff against dist). There is no cron/ schedule wired up for this yet; run it ad hoc when you want the fleet updated, or add a crontab entry yourself (a starting point is documented in update-packages.yml's header).

To pin a package to an exact version, or deliberately bump one named package to latest outside this schedule, copy the pattern in update-packages-pinned-example.yml.

Description
No description provided
Readme 85 MiB
Languages
Jupyter Notebook 79.8%
Python 13.3%
Jinja 3.2%
C++ 1.7%
Shell 1.3%
Other 0.6%