Full rearchitecture using Claude

- event bus systen
- all components are independent
- preparation for web frontend
This commit is contained in:
2026-08-26 13:22:28 +02:00
parent 55bcc9448c
commit d44c24ec97
70 changed files with 7591 additions and 1011 deletions

View File

@@ -1,42 +1,82 @@
# MusicMouse — repo overview
Orientation doc for AI agents (or humans) working on this repo for the first time. Written from what's actually in the repo — no roadmap speculation beyond `esp-firmware/todo.md`.
Orientation doc for AI agents (or humans) working on this repo for the first time.
## What this is
MusicMouse is a DIY, Toniebox-style physical music player for kids, shaped like a mouse and living on a shelf. Small 3D-printed animal figurines (fox, owl, dog, elephant, squirrel, crocodile, rabbit, snowman, puppy — see `hardware/3dprints/figures/`) each carry an RFID tag. Placing a figurine on the mouse triggers an RFID read, which starts that figure's music playlist. The mouse also has a rotary encoder + touch buttons (ears/feet) for volume/skip control, addressable RGBW LED rings with animated effects, and MQTT/Home Assistant integration so a "shelf light" shows up as a smart-home device.
MusicMouse is a DIY, Toniebox-style physical music player for kids, shaped like a mouse
and living on a shelf. Small 3D-printed animal figurines (fox, owl, dog, elephant,
squirrel, crocodile, rabbit, snowman, puppy — see `hardware/3dprints/figures/`) each
carry an RFID tag. Placing a figurine on the mouse starts that figure's playlist. The
mouse also has a rotary encoder and capacitive touch areas (ears/feet) for
volume/skip control, addressable RGBW LED rings with animated effects, and MQTT/Home
Assistant integration.
## Repo layout
| Path | What it is |
|---|---|
| `python-backend/` | Python host application — the main runtime. Reads RFID/button/encoder events from the ESP32 over serial, drives playback via VLC, sends LED effect commands, bridges to MQTT/Home Assistant. Start here for backend work. |
| `python-backend/` | Python host application — the main runtime. Has its own `README.md` with the architecture; start there for backend work. |
| `esp-firmware/` | ESP32 firmware (C++, Arduino framework via PlatformIO). Reads the RFID reader and buttons, drives the LED strips, talks to `python-backend` over serial. |
| `hardware/` | 3D-print models for the figurines and enclosure (FreeCAD/Blender/OBJ/STL), a Fritzing electronics sketch, datasheets, and `pinout.md` (RFID reader + button-board wiring). |
| `claude-design/` | An unrelated exploratory web-UI mockup ("Dolphin Beats Music Player" / rebrand concept). Not integrated with the rest of the repo — no build system ties it in. Don't assume it reflects current product direction. |
| `.vscode/` | Editor settings (C++ header associations). |
There is no top-level README elsewhere in the repo; this file plus `esp-firmware/todo.md` and `hardware/pinout.md` are the only prose docs.
| `claude-design/` | An unrelated exploratory web-UI mockup ("Dolphin Beats"). Not integrated with anything — no build system ties it in. Don't assume it reflects current product direction. |
## How the pieces talk to each other
- **ESP32 firmware ↔ `python-backend`**: a length-prefixed binary protocol over serial (`pyserial-asyncio`), implemented in `python-backend/host_driver.py`. Messages are framed with magic tokens (`MAGIC_TOKEN_HOST_TO_FW`/`MAGIC_TOKEN_FW_TO_HOST`) and a `struct`-packed header. The message-ID maps and struct formats in `host_driver.py` must stay byte-for-byte in sync with the firmware's `esp-firmware/src/Messages.h` — there's no shared schema or test verifying this cross-language contract, so a firmware protocol change can silently desync the Python side.
- **`python-backend` ↔ MQTT/Home Assistant**: `python-backend/mqtt_json.py` exposes the shelf LED strip as a Home-Assistant-discoverable JSON-schema MQTT light (`ShelveLightMqtt`), and `main.py` also calls Home Assistant services directly (e.g. toggling room lights) via `hass-client`.
- **ESP32 firmware ↔ `python-backend`**: a length-prefixed binary protocol over serial.
Frames are `uint32 magic | uint8 type | uint16 size | payload`, little-endian, with
firmware log text interleaved on the same link. The Python side lives in
`python-backend/musicmouse/devices/wire.py`, the firmware side in
`esp-firmware/src/Messages.h`.
The contract is hand-duplicated in two languages. `tests/test_wire.py` parses
`Messages.h` and fails if the message ids drift, and `tests/test_effects.py` pins the
exact bytes of every effect payload — so a firmware change that breaks the host now
breaks a test instead of just the LEDs.
- **`python-backend` ↔ Home Assistant**: MQTT only. The backend publishes three
discoverable lights, a player sensor, a volume number, transport buttons, device
triggers for every button and touch area, and a tag scanner for the RFID reader. It
does *not* call Home Assistant services directly any more; behaviour like "the left
ear turns the room light pink" is an HA automation. See `python-backend/README.md`
for the trigger topics and the old colour mapping.
## Running it
```
python python-backend/main.py <config_dir>
```sh
python -m musicmouse --config /media/musicmouse/config.yml
```
`main.py` expects `<config_dir>/config.yml` (schema documented in the new `python-backend/config.yml.example` — no real config was previously checked in or documented). In production this is deployed as a systemd service reading music from `/media/musicmouse/`; see `esp-firmware/musicmouse.service` for the unit template — **note its `ExecStart` path (`.../espmusicmouse/host_driver/main.py`) is stale**, referencing the pre-reorg directory layout from before the `bd8925a "Cleaned up repository"` commit moved things to `python-backend/`. Update that path before relying on the service file.
Or, with no hardware and no audio:
## Config schema (see `python-backend/config.yml.example`)
```sh
python -m musicmouse --config ./config.yml --simulate
```
- `general.{alsa_device, serial_port, hass_url, hass_token, mqtt.{server,user,password}, min_volume, max_volume, volume_increment, button_leds_brightness}`
- `figures.<name>.{id, colors, media_files}``id` is a hex RFID tag id, `colors` is a list of 4 colors (`primary, secondary, background, accent`, each `"#rrggbb"` or `"wNN"`), `media_files` is optional (auto-globbed from the config dir by figure name if omitted).
The simulator runs the whole app against a fake serial link and a fake player, either
interactively or from a scenario file. `python-backend/musicmouse.service` is the
systemd unit for the device.
## Known gaps / notes for agents
## Config
- **No automated tests, no lint/formatter config, no CI** anywhere in the repo (neither `python-backend/` nor `esp-firmware/`, aside from a PlatformIO `native` build env for firmware unit testing).
- `python-backend/audio_analysis.py` and the three chord-recognition notebooks (`C5S2_ChordRec_Templates.ipynb`, `C5S3_ChordRec_HMM.ipynb`, `C5S3_HiddenMarkovModel.ipynb`) are university-course exploratory material (chroma/chord-recognition DSP), **not imported by `main.py`** and not part of the running app. They reference stale personal absolute paths.
`python-backend/config.yml.example` documents the schema. In short:
- `general.{figure_folder, serial_port, baudrate, reconnect_interval, alsa_device,
min_volume, max_volume, initial_volume, volume_increment, button_leds_brightness,
audio_extensions, mqtt.*}`
- `figures.<name>.{id, colors}` — `id` is a 5-byte hex RFID tag, unique per figure;
`colors` is exactly four (`primary, secondary, bg, accent`), each `"#rrggbb"` or
`"wNN"`.
Each figure plays `<figure_folder>/<figure name>`, alphabetically by filename. Config is
validated with pydantic: unknown keys are errors, and every problem is reported at once.
## Notes for agents
- The backend has `pytest`, `ruff` and `mypy --strict` configured in
`python-backend/pyproject.toml`, and no CI. Run all three before proposing changes.
- Prefer adding a scenario in `python-backend/scenarios/` over a hand-rolled test when
the behaviour is end-to-end — those files are executed by the test suite.
- `python-backend/notebooks/` is university course material on chord recognition, not
part of the app.
- The firmware has no automated tests beyond a PlatformIO `native` env for LED effects.