# Example config for the MusicMouse backend. # # python -m musicmouse --config /media/musicmouse/config.yml # # Unknown keys are rejected rather than ignored, and every problem in the file is # reported at once, so a typo fails at startup with the path to the offending line. # Keep the real config (with credentials) off the repo - on the device only. general: # The music collection. One path; the shelves underneath it are fixed names, not # settings, because each one has its own quirks the code already knows about: # # /Figuren/
/ one folder per figurine # /Musik/ - / albums, grouped by artist # /Hörbücher/ - / audiobooks, grouped by character # /Kinderpodcasts// shows, newest episode first # # A cover.jpg next to the audio is used if present, otherwise the art is pulled out # of the files' tags. Relative paths resolve against this file's directory. library: root: /home/martin/Music # Scan results, extracted cover art and track analysis. Safe to delete: the index # is rebuilt on the next start. Deleting it does throw away track analysis, which # is expensive to recompute. cache: .musicmouse-cache # How many tracks the background analyzer may work on at once, each in its own # worker process. Omitted means one per core bar one (capped at 8), which is what # turns a first-time pass over a whole library from an overnight job into a coffee # break on a desktop. Set it to 1 on a machine that has better things to do, or to # a specific number to cap how much of it analysis may take. # analysis_workers: 4 # Serial port the ESP32 firmware is on. A dropped link is retried, not fatal. # Required - use "simulate" to run without the mouse attached, which is a complete # setup on its own because the web front-end can drive the player by itself. RFID, # buttons and LEDs then do nothing, and startup says so every boot. serial_port: "/dev/ttyUSB0" baudrate: 115200 reconnect_interval: 5.0 # ALSA output device passed to VLC, e.g. "hw:0,0", or "default" for the system # default output. Required - use "simulate" for a player that makes no sound, which # is handy when working on the web UI on a machine whose audio you would rather not # commandeer. Startup says so every boot. # # Both of these are 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. alsa_device: "softvol_effects" # Volume, 0..100. min/max clamp everything, including the rotary encoder. min_volume: 0 max_volume: 60 initial_volume: 40 volume_increment: 5 # per rotary-encoder click # Backlight of the prev/next buttons while a figure is playing, 0..1. button_leds_brightness: 0.5 # Which files count as music. Anything else - a podcast downloader's archive.json, # a half-finished .tmp - is ignored. audio_extensions: [".mp3", ".ogg", ".oga", ".opus", ".flac", ".wav", ".m4a", ".aac"] # The web front-end. Omit the whole section to run without it. # # There is no authentication: this is a device on a home network. The settings panel # at ?parentMode=1 is hidden from the child, not protected from them - it writes back # to this file. Put it behind a reverse proxy if that is not good enough. web: host: "0.0.0.0" port: 8080 # Built frontend to serve at /. Omit to expose only the JSON API. static_dir: ../web/dist # IR remote control, over lircd's TCP socket (see ansible/roles/pi_lirc for how # lircd itself is set up on the Pi). Omit the whole section to run without a remote. # Play/pause/stop/previous/forward/rewind/volume/mute map to normal music control; # number keys 0-9 play whatever the "remote:" section below assigns them. lirc: host: "musicmouse-pi.local" port: 2222 # this deployment's lircd listens on 2222, not its own # default of 8765 - see the ansible role remote_name: "Hauppauge" # other remotes registered with the same lircd (an LED # remote, say) are ignored reconnect_interval: 5.0 # Home Assistant integration. Omit the whole section to run without MQTT. # The backend exposes three lights, a player sensor, a volume slider, transport # buttons, device triggers for every button/touch area, and a tag scanner. mqtt: server: "homeassistant.local" port: 1883 user: "musicmouse" password: "REPLACE_WITH_MQTT_PASSWORD" base_topic: "musicmouse" discovery_prefix: "homeassistant" device_id: "musicmouse" device_name: "Music Mouse" reconnect_interval: 10.0 # Room control page ("Mein Zimmer"). Omit the whole section to hide the page. This is # the opposite direction from mqtt above: it's musicmouse controlling Home Assistant # entities, not the other way round. The backend proxies every call to Home # Assistant's REST API with this token attached; the browser never sees it, only # entity ids and display names. Home Assistant's own CORS settings do not need to # allow musicmouse's origin for this - the browser only ever talks to musicmouse. ha: url: "http://homeassistant.local:8123" # A long-lived access token, created under the HA user's own profile page. token: "REPLACE_WITH_HA_LONG_LIVED_TOKEN" # Cards on the room page, in this order. "name" is optional; falls back to the # entity id if omitted. devices: - entity_id: cover.kinderzimmer_rollo name: "Rollo" - entity_id: light.kinderzimmer_hue_beyond_links name: "Hue Beyond links" - entity_id: light.kinderzimmer_deckenlampe name: "Deckenlampe" # Scene pill row above the cards, in this order. scenes: - entity_id: scene.kinderzimmer_lesen name: "Lesen" - entity_id: scene.kinderzimmer_gute_nacht name: "Gute Nacht" # The typing game ("Tippen"). Omit the whole section to hide its tab in the web # front-end. The lesson plan is content, not device config, so it lives in its own # file - see tippen-curriculum.yml.example for the format, including the optional # `unlocks:` key that turns passing a lesson into unlocking part of the library. tippen: curriculum_file: tippen-curriculum.yml # Where progress (stars, unlocked lessons, streak, ...) is saved. Written by the # app itself - never hand-edited. Relative to this file, like curriculum_file. progress_file: tippen-progress.json # One entry per figurine. The key is the figure name and the subfolder name. figures: fuchs: # RFID tag id, 5 bytes as hex. Must be unique across figures. id: "04a1b2c3d4" # Exactly four colours: primary, secondary, background, accent. # Either "#rrggbb" (RGB) or "wNN" (white channel only, hex). colors: ["#ff6600", "#ffcc00", "#331100", "wff"] # "music" (default) or "book". Every other shelf is named after what is on it, so # its type is obvious; a figure folder is named after the figurine, so this is the # one thing that has to be said out loud. The web UI draws albums square and # audiobooks taller than wide, so getting it wrong is visible at a glance. kind: music eule: id: "04b2c3d4e5" colors: ["#3355ff", "#66aaff", "#001133", "#ffffff"] kind: book # Number keys 0-9 on the IR remote, mapped to what they play. Omit the whole section, # or any digit within it, for "unassigned" - a fresh install boots with none of this # and that is not an error. Editable from the web front-end, which writes back here. # # target_kind: album -> always starts from the first track (music, audiobooks). # target is an album id, as shown at GET /api/library. # target_kind: series -> always plays the newest episode of a podcast show, resolved # fresh on every press - never a fixed episode. target is the # show's folder name under Kinderpodcasts, e.g. "Wissen macht Ah". remote: "1": target_kind: album target: "3f9a0c12ab44" "2": target_kind: series target: "Wissen macht Ah"