Files
ansible/roles/pi_musicmouse/files/config-musicdolphin.yml
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

94 lines
4.2 KiB
YAML

---
# musicdolphin: display-only instance. No physical mouse attached, so serial and audio
# are both "simulate" - the web front-end (shown locally in the pi_kiosk Firefox window,
# and reachable on the LAN for a tablet) is a complete way to drive the player on its
# own. `figures` still needs at least one entry (the schema requires it) even though
# nothing will ever scan a tag here.
#
# This Pi does have a HifiBerry amp (mediapi_has_hifiberry_amp, alsa_card_name
# sndrpihifiberry), so the `softvol_effects` slot from pi_alsasetup exists on it.
# Swapping alsa_device to that is the one-line change that gives this instance real
# sound - the amp is currently shared by squeezelite and shairport only.
#
# Unknown keys are rejected rather than ignored, so a typo fails at startup with the
# path to the offending line. Every key is documented in the app repo's
# python-backend/config.yml.example.
general:
library:
# The four shelves underneath are fixed names, not settings: Figuren/, Musik/,
# Hoerbuecher/ (with real umlauts) and Kinderpodcasts/.
root: /media/musicmouse
# Relative, so it resolves against *this file's* directory -
# /media/musicmouse/.musicmouse-cache. Seeded by the app repo's
# scripts/sync-library.sh; safe to delete, at the cost of recomputing analysis.
cache: .musicmouse-cache
# analysis_workers: 2
# ^ pointless until this role installs the backend's `analysis` extra
# (librosa + numpy, a heavy build on a Pi). Without it build_analyzer() falls
# back to NullAnalyzer and the reactive background stays static.
# Both required rather than optional on purpose: running blind or silent has to be
# asked for, so a config that lost a line fails loudly instead of booting into
# something that looks like it is working. Startup says so every boot.
serial_port: "simulate"
alsa_device: "simulate"
web:
host: "0.0.0.0"
port: 8080
# Absolute on purpose. The repo's example uses ../web/dist because its config sits
# in python-backend/; from /media/musicmouse that would resolve to /media/web/dist
# and silently serve nothing.
static_dir: /opt/musicmouse/web/dist
# The typing game. Both paths are relative, so both land in /media/musicmouse/.
# curriculum_file must exist at startup and is installed by this role;
# progress_file is created and written by the app itself.
tippen:
curriculum_file: tippen-curriculum.yml
progress_file: tippen-progress.json
# Home Assistant "Mein Zimmer" page. Left out until the Wohnzimmer-oben entity ids
# are picked - the dev config's entities are Arbeitszimmer, the wrong room for this
# Pi. An empty ha: block is invalid; the section needs at least one device or scene,
# so uncomment it only once it is filled in:
#
# ha:
# url: "https://ha.bauer.tech"
# token: "<long-lived access token>"
# devices:
# - entity_id: cover.wohnzimmer_oben_rollo
# name: "Rollo"
# - entity_id: light.wohnzimmer_oben_...
# name: "Licht"
# scenes:
# - entity_id: scene.wohnzimmer_oben_...
# name: "Lesen"
# No mqtt: section. The Home Assistant integration exposes the *mouse's* lights,
# buttons, touch areas and tag scanner, none of which exist on a simulate-only
# instance - it would publish a device of dead entities.
#
# No lirc: section either, and consequently no top-level `remote:` mapping. This host
# uses mediapi_ir_control: irserver, a custom serial daemon on /dev/ttyUSB0; the
# backend's LircConfig speaks lircd's TCP protocol (pi_lirc, port 2222) and cannot
# talk to it.
# Backlight of the prev/next buttons while a figure is playing, 0..1. Inert here
# (no mouse), kept so the file reads the same as the other hosts'.
button_leds_brightness: 0.5
# Volume, 0..100. The full range: this one is driven from a screen, not from a knob a
# child can spin, so there is nothing to protect against.
min_volume: 0
max_volume: 100
initial_volume: 40
volume_increment: 5
figures:
# Placeholder - serial_port is "simulate" so no reader ever reports a tag id, but the
# schema requires at least one figure to be configured.
placeholder:
id: "0000000001"
colors: ["#ffffff", "#ffffff", "#000000", "#ffffff"]