Files
ansible/roles/pi_musicmouse/README.md
Martin Bauer 9bd23467f0 Build the venv from the distribution's Python instead of uv
The backend now targets 3.11, which is what Raspberry Pi OS ships, so there is
nothing left for uv to solve. It was only ever there to obtain a 3.13 the distro
does not have, and it brought its own problems: the download had to be matched
to the Pi's 32-bit userland by hand, and no armv7 wheel exists for a 3.13 ABI,
so Pillow was compiled from source and needed image headers installed alongside.

apt for the interpreter, python3 -m venv, pip for the rest - and piwheels then
supplies prebuilt armhf wheels for the native dependencies.

A venv cannot be migrated between interpreters (it holds absolute paths into the
one that made it), and `creates:` would keep the old one forever, so check what
built it and rebuild when it does not match the current python3. That is what
carries a device off the uv-installed 3.13 without hand-holding.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 21:21:12 +02:00

70 lines
3.9 KiB
Markdown

# pi_musicmouse
Deploys the MusicMouse backend (`python-backend/`) and its built web frontend.
- Checks out `musicmouse_version` from `pi_musicmouse_repo` to `/opt/musicmouse`, then
**fails early** if that checkout has no `python-backend/pyproject.toml` - see
"Which version" below.
- Creates `/opt/musicmouse/.venv` from the distribution's own `python3` and pip-installs
the backend into it. The backend targets the Python that Raspberry Pi OS ships (3.11
on Bookworm) precisely so this step stays boring: apt and piwheels have prebuilt
armhf wheels for the native dependencies, so nothing has to be compiled on the Pi.
If the venv was built by a different interpreter than the current `python3`, it is
thrown away and rebuilt.
- Builds the frontend (`web/`) on the *control machine* (delegated to `localhost`, not
the Pi - no Node/npm is installed on the Pi) and syncs `web/dist` over.
- Installs `/media/musicmouse/config.yml` from `files/config-<inventory_hostname>.yml`
(one file per host - the schema differs between a host with a real mouse attached and
a display-only one; see those files' own comments), the typing game's curriculum
beside it, and the systemd unit copied straight from the checked-out repo's
`python-backend/musicmouse.service`, so it can never drift from what's actually in the
app repo.
- Sets up a Samba share of `/media/musicmouse` for copying music onto the device.
## Which version
`musicmouse_version` is set per host in `host_vars`, with no default, and points at a
`deploy/<host>` branch on the remote. Those branches are the deployment record: what is
installed on that box right now. Moving one is a force-push, and the next run follows it
(the `git:` task uses `force: true`):
git push -f origin rearchitect-backend:deploy/musicdolphin
just mediapis musicdolphin
This role only understands the post-rearchitecture layout - `python-backend/` as an
installable package plus a `web/` frontend. On older commits the host driver lives at
`espmusicmouse/host_driver/main.py`, there is no `pyproject.toml`, the unit file is
under `esp-firmware/`, there is no frontend at all, and the config is a different format
that this role's config files would fail to load. There is deliberately no legacy path:
the role stops at the checkout with an explanation instead.
`musicmouse` (Kinderzimmer) is on such an older commit and still working, so it has
`mediapi_install_kidsmusic: false` and this role skips it entirely; `deploy/musicmouse`
exists purely as the rollback marker for what is installed there. Migrating it means
moving `/media/musicmouse/<figure>/` into `Figuren/` and switching to the new config
schema - `files/config-musicmouse.yml` is already written for that.
## config.yml is install-once
The app writes to its own config at runtime: parent mode (`?parentMode=1`) patches the
volume keys, and the remote-control page rewrites the whole `remote:` block. A plain
`copy:` would throw those away on every ansible run, so the config task is
`force: "{{ pi_musicmouse_force_config }}"`, default `false`. Push a changed config when
you actually mean to:
just run mediapis.yml musicdolphin -e pi_musicmouse_force_config=true
The curriculum file is not written by the app, so it is installed unconditionally. A
config with a `tippen:` section will not start without it - `curriculum_file` is
validated as must-exist - so a host whose config has no `tippen:` section should set
`pi_musicmouse_curriculum_file: ""`.
## Manual steps after a first deploy
- Set the Samba password with `smbpasswd`.
- Get the music onto the device. The app repo's `scripts/sync-library.sh` is the way to
do it: as well as the library it carries over the cover and track-analysis cache,
which is the part that costs minutes of DSP per track to recompute, and it knows which
parts of that cache survive the move and which must not be copied. The Samba share
works too, and then the device does all that work itself.