Metadata-Version: 2.4
Name: minidsp-dash
Version: 0.1.0
Summary: Phone-friendly web remote for miniDSP devices: presets, gain, mute, source and live meters
Author: Matt
Project-URL: Homepage, https://github.com/av-dsp-tools/minidsp-dash
Project-URL: Issues, https://github.com/av-dsp-tools/minidsp-dash/issues
Keywords: minidsp,minidsp-rs,dsp,audio,hifi,remote,web,meters
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Home Automation
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# minidsp-dash

A **phone-friendly web remote** for miniDSP devices — presets, master gain, mute, source switching, and live input/output meters — in a single dependency-free Python file on top of the [`minidsp` CLI (minidsp-rs)](https://github.com/mrene/minidsp-rs).

miniDSP's own config tools are desktop apps; this gives you the day-to-day controls on your phone. Add it to your home screen and it behaves like an app (dark UI, safe-area aware, auto-reloads itself when you bump `APP_VERSION`).

- **Presets** — 4 buttons, labels you choose
- **Master gain** — big slider with live dB readout (cut-only, floor configurable)
- **Mute** — one thumb-sized toggle
- **Source** — analog / toslink / usb (configurable)
- **Meters** — live input/output levels at **8 Hz** with proper ballistics (instant attack, ~1.5 s decay, peak-hold markers) when `minidspd` is running; graceful ~1.5 s fallback via the CLI when it isn't
- **Zero dependencies** — Python 3.8+ stdlib; the only requirement is a working `minidsp` CLI

## Setup

1. Install [minidsp-rs](https://github.com/mrene/minidsp-rs) and check `minidsp` prints your device's status.
2. Run the dashboard, either from a checkout:

```bash
python3 minidsp_dash.py
# open http://<host>:5390 from your phone
```

or from PyPI, which puts a `minidsp-dash` command on your PATH:

```bash
pip install minidsp-dash
minidsp-dash
```

Both run the same single file and pull in nothing else — the package has no
Python dependencies. The `minidsp` CLI is the only requirement, and it is not a
Python package, so it has to be installed separately either way.

Configuration is all environment variables:

```bash
PRESET_NAMES="Flat,EDM,Movies,Late Night" \
SOURCES="analog,toslink,usb" \
GAIN_MIN=-60 \
DASH_TOKEN=some-secret \
DASH_PORT=5380 \
python3 minidsp_dash.py
```

| Variable | Default | Meaning |
|---|---|---|
| `MINIDSP_BIN` | `minidsp` (PATH) | Path to the CLI; point it at a wrapper script to use `minidsp --url` against a remote minidspd |
| `DSPD_URL` | `http://127.0.0.1:5380/devices/0` | minidspd's HTTP API — the fast meter path (~7 ms/poll vs a CLI spawn). Meters run at 8 Hz through it and fall back to CLI status automatically when unreachable |
| `DASH_PORT` | `5390` (5380 is minidspd's own HTTP API — avoid it) | Listen port |
| `DASH_TOKEN` | *(off)* | If set, requests need `?t=<token>` (open the page as `http://host:5390/?t=<token>`) |
| `PRESET_NAMES` | `Preset 1..4` | Comma-separated labels for the preset buttons |
| `SOURCES` | `analog,toslink,usb` | Which sources to offer |
| `GAIN_MIN` | `-60` | Bottom of the gain slider (dB) |

> **Auth note:** the token gates control of your DSP but travels in plain HTTP on your LAN. Fine for a home network; don't port-forward it to the internet.

### Run at login (macOS)

Save as `~/Library/LaunchAgents/com.minidsp.dash.plist`, then `launchctl load` it:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
  <key>Label</key><string>com.minidsp.dash</string>
  <key>ProgramArguments</key><array>
    <string>/usr/bin/python3</string>
    <string>/PATH/TO/minidsp-dash/minidsp_dash.py</string>
  </array>
  <key>EnvironmentVariables</key><dict>
    <key>PRESET_NAMES</key><string>Flat,EDM,Movies,Late Night</string>
  </dict>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict></plist>
```

`KeepAlive` restarts the agent if it *crashes*, but not when you edit
`minidsp_dash.py` — the process keeps serving whatever it loaded at launch. After
editing, restart it explicitly:

```bash
launchctl kickstart -k gui/$(id -u)/com.minidsp.dash
```

Skipping that is a genuinely confusing bug to chase: the source on disk shows
a fix the running server has never loaded. If a symptom contradicts the code,
compare the process start time (`ps -Ao pid,lstart | grep minidsp_dash.py`) against
the file's mtime before debugging anything else.

On Linux, an equivalent systemd user unit works the same way.

## How it works

The server shells out to the `minidsp` CLI for everything — status is parsed from the CLI's `MasterStatus { ... }` line plus the input/output level lines, writes are `minidsp config N`, `minidsp gain -- -X.X`, `minidsp mute on|off`, `minidsp source <name>`. No USB/HID code here, so anything minidsp-rs supports (2x4HD, Flex, SHD, DDRC-24, remote minidspd, ...) should work; presets/gain/mute/source are the common denominator.

The page polls `/api/status` every ~1.5 s; controls are optimistic (they render instantly and reconcile against device truth). Meters have their own fast path: `/api/meters` proxies minidspd's HTTP API (`DSPD_URL`) at 8 Hz, and the client applies meter ballistics — instant attack, ~1.5 s full-scale decay, a peak-hold marker that holds 1.5 s then falls. When minidspd isn't reachable the bars quietly revert to the ~1.5 s CLI numbers. `APP_VERSION` is stamped into the page and the status JSON — a stale cached client notices the mismatch and reloads itself once, which matters for iOS home-screen apps that cache aggressively.

## Troubleshooting

- **Meters sluggish?** You're on the CLI fallback — check `minidspd` is running and `DSPD_URL` points at it.
- **Device healthy but silent, inputs stuck at noise floor (~−86 dB)?** A USB DAC feeding your analog inputs can wedge in a way that's invisible to the OS (streaming happily, nothing coming out). Two resets that fix it in practice: toggle the DSP's source away and back (`minidsp source usb && sleep 1 && minidsp source analog`), and/or switch your computer's output device away and back to rebuild the USB stream. Check the DAC's front panel before blaming cables.
- **Meters legitimately read the noise floor when nothing is playing** — silence at the inputs is not a fault. Verify against a playing source before concluding anything is broken.

## Related projects

- [minidsp-rs](https://github.com/mrene/minidsp-rs) — the CLI/daemon this sits on
- [minidsp-rs-hass](https://github.com/xuio/minidsp-rs-hass) — Home Assistant integration
- Sibling project: [biamp-ntp](https://github.com/av-dsp-tools/biamp-ntp) — control Biamp Nexia/Audia DSPs from Python

## Development

```bash
python3 -m unittest discover -s tests -v
```

The tests cover status parsing against canned CLI output; no hardware needed.

## License

MIT.
