Plan for SimpleDances reachy mini app (v1) ========================================= 0. Context & goals ------------------ - Reuse the structure of `reachy_mini_radio` to build a Reachy Mini app with a custom UI hosted from `SimpleDances/SimpleDances/static`. - Drive actual motions using `reachy_mini_dances_library`, exposing *all* available moves with tunable parameters. - Provide a clean web UI with dance selection, parameter sliders + numeric inputs, start/stop toggle, BPM + amplitude sliders, reset-to-default per dance, and a rhythmic visualization tied to all 8 DoF (6 head + 2 antenna) using live data from the control loop. - Persist parameter tweaks so each dance remembers its last settings between runs. 1. Backend design (SimpleDances/SimpleDances/main.py) ---------------------------------------------------- - Import `AVAILABLE_MOVES` + antenna metadata from the dance library, and pre-compute a catalog: name, friendly label (title case), description, default params, metadata (duration, etc.). - Build helpers to: * Load/save persisted parameters (JSON file adjacent to main.py). Structure: `{ "globals": {"bpm": float, "amplitude_scale": float}, "dances": {dance_name: {param: value}} }`. * Merge saved params with defaults per dance. * Derive UI specs for each param (type, slider min/max/step, unit, display multiplier) using heuristics based on the param name (e.g. `_rad` => degrees slider ±(default*3 capped), `_m` => centimeters range, `subcycles` => 0-4, `phase_offset` => 0-1, enumerations for waveform/antenna moves). * Convert between internal units and UI units when serving values + when updates arrive. - Shared runtime state guarded by a `threading.Lock`: selected dance name, per-dance params, BPM, amplitude scale, playing flag, timestamps, last computed offsets for visualization, file persistence timestamps, etc. - Settings API endpoints on `self.settings_app`: * `GET /api/dances` -> list of dances (name, label, description). * `GET /api/state` -> current selection, playing flag, bpm, amplitude scale, parameter specs for the selected dance (values already converted for UI), and last-saved timestamp. * `POST /api/select` -> pick a dance (payload: `{name}`), responds with updated param specs + selection. * `POST /api/params` -> update parameters for the (selected) dance; payload includes `name` + `params` dictionary in UI units; convert, persist, respond with new specs. * `POST /api/globals` -> update BPM and amplitude scale with validation + persistence. * `POST /api/toggle` -> set playing flag (payload `{"playing": bool}`) and resets timing when toggling on. * `POST /api/reset` -> reset a dance back to defaults (payload `{"name": ...}`) with save + updated specs. * `GET /api/visualization` -> return most recent normalized offsets (position/orientation/antennas arrays) for front-end animation plus playing + bpm (read-only, polled at ~10–15 Hz by UI). - Control loop logic: * On `run`, load ReachyMini, ensure motors enabled as needed (maybe only when playing), instantiate start time. * Inside loop (50–100 Hz), read `state` snapshot; if playing and a dance is selected, compute beat time using global BPM, call the corresponding move function with stored params, scale offsets by amplitude scale, convert to head pose with `create_head_pose`, and send `reachy_mini.set_target(head=head_pose, antennas=...)`. If stopped, gradually go to neutral pose (or hold last command) to avoid abrupt freeze. * Update shared `latest_offsets` for visualization each cycle, store for `/api/visualization`. * Sleep for ~0.02s. - Persist JSON whenever params/globals change (debounce by writing immediately under lock). - Handle graceful shutdown (stop flag already provided); ensure motors disabled on exit. 2. Front-end (SimpleDances/SimpleDances/static/{index.html,style.css,main.js}) ----------------------------------------------------------------------------- - Layout: split page with header, global controls, dance selector, parameter panel, start/stop button, and visualization canvas/bars. - Use CSS with pastel gradient, glassmorphism cards, responsive grid for parameter controls. - JS boot flow: * Fetch `/api/dances` once for the list (populate `