@@ -25,7 +25,7 @@ Everything it does fits on one screen:
|
||||
| --- | --- | --- |
|
||||
| Living Room | 2× Denon Home 200 as an In-Room Group | a **group** (`gid`) |
|
||||
| Lego Room | Denon Home 400 | a **player** (`pid`) |
|
||||
| Home Cinema | Denon AVR-X3800H | a **player**, plus Telnet on port 23 |
|
||||
| Home Cinema | Denon AVR-X3800H | a **player** |
|
||||
|
||||
Any other mix works — it is all in `config.py`.
|
||||
|
||||
@@ -67,15 +67,16 @@ ROOM_KEYS = ["home400", "living_room_group"] # the cards, in order
|
||||
```
|
||||
|
||||
`HEOS_HOST` only needs to point at **one** device: HEOS is distributed, so any
|
||||
unit can see and control the whole network. `AVR_HOST` must be the AVR itself.
|
||||
unit can see and control the whole network — including the AVR's own inputs,
|
||||
so there is nothing AVR-specific to configure beyond its entry in `TARGETS`.
|
||||
|
||||
`VOLUME_STEP` is the grid the volume buttons snap to, not simply how much
|
||||
they add: at 5, a tap moves 23 to 25 and 25 to 30.
|
||||
|
||||
The input picker already leaves out sources switched off in the AVR's own
|
||||
setup menu (it asks the AVR with `SSSOD ?`). `AVR_INPUT_CODES` narrows it
|
||||
further to the sources you actually use, and sets their order; leave it empty
|
||||
to list everything the AVR still has switched on.
|
||||
`AVR_INPUT_CODES` narrows the input picker to the sources you actually use,
|
||||
by their HEOS input id (`GET /api/avr/inputs` shows the exact strings, e.g.
|
||||
`inputs/aux_in_1`), and sets their order; leave it empty to list everything
|
||||
HEOS reports for it.
|
||||
|
||||
## Add it to the iOS home screen
|
||||
|
||||
@@ -87,8 +88,9 @@ if you ever put it behind a domain name, give it HTTPS.
|
||||
|
||||
## Run it as a service
|
||||
|
||||
`deploy/heos-panel.service` runs the panel out of its own virtualenv and
|
||||
restarts it if it dies:
|
||||
`deploy/heos-panel.service` runs the panel out of its own virtualenv, under
|
||||
`gunicorn` rather than `python3 app.py`'s dev server, and restarts it if it
|
||||
dies:
|
||||
|
||||
```bash
|
||||
sudo cp deploy/heos-panel.service /etc/systemd/system/
|
||||
@@ -98,6 +100,10 @@ sudo systemctl enable --now heos-panel
|
||||
|
||||
Edit `User=` and the paths in it if you keep the panel somewhere else.
|
||||
|
||||
`python3 app.py` (no gunicorn) is still the right way to run it by hand
|
||||
while working on it — see `--demo` below — the dev-server warning it prints
|
||||
is expected there and only matters for the service above.
|
||||
|
||||
## Deploying from Gitea
|
||||
|
||||
`.gitea/workflows/deploy.yml` checks out the push, runs the tests, rsyncs
|
||||
@@ -235,20 +241,25 @@ a restart. If you would rather pin them down, list them in `config.py`:
|
||||
Leaving a room deliberately does *not* rewrite the AVR's group, so the other
|
||||
room's music does not restart.
|
||||
|
||||
## Two protocols, not one
|
||||
**A newly joined room can stay silent on the AVR's input.** Joining alone
|
||||
does not push audio to it — HEOS needs telling *again* which input is
|
||||
playing before it streams that input to the new member, the same reselect
|
||||
you'd otherwise do by hand in the HEOS app (Home → Sources → AV). `join()`
|
||||
does this for you: it reads the AVR's current input back and replays it
|
||||
through `browse/play_input` right after the group merge.
|
||||
|
||||
| | HEOS CLI (port 1255) | Denon Telnet (port 23) |
|
||||
| --- | --- | --- |
|
||||
| Speaks | JSON, `heos://player/...` | plain text, `SIGAME`, `SSFUN ?` |
|
||||
| Used for | players, groups, volume | the AVR's **renamed** input list |
|
||||
## One protocol, not two
|
||||
|
||||
HEOS only knows generic input ids like `inputs/hdmi_in_1`; the names you gave
|
||||
your sources live in the AVR's own protocol, which is why both are here.
|
||||
Everything goes over the HEOS CLI (port 1255) — players, groups, volume, and
|
||||
the AVR's own inputs. `browse/browse` on the AVR's pid lists its inputs under
|
||||
whatever names you gave them in its setup menu; HEOS reports those renamed
|
||||
labels itself, so there used to be a second client here for the AVR's Denon
|
||||
Telnet port just to fetch them, and it is not needed any more.
|
||||
|
||||
The Telnet connection is held open, so input changes made with the physical
|
||||
remote show up in the panel too. Some Denon models only accept **one** Telnet
|
||||
connection at a time — if another integration (Home Assistant, say) already
|
||||
holds it, the AVR card will read `offline` while the HEOS half keeps working.
|
||||
Selecting an input goes through `browse/play_input`, not a raw `SI<code>`
|
||||
Telnet command, for the same reason joining a room re-sends it (see above):
|
||||
that is what actually tells HEOS to *stream* the input to whichever players
|
||||
are grouped with the AVR, not just which jack the AVR itself is listening to.
|
||||
|
||||
## HTTP API
|
||||
|
||||
@@ -261,10 +272,11 @@ Used by the interface:
|
||||
| `POST /api/volume` | `{"target": "home400", "steps": 1}` — taps, snapped to `VOLUME_STEP`. Also takes `delta` (raw points) or `level` (absolute) |
|
||||
| `POST /api/mute` | `{"target": "home400"}` |
|
||||
| `POST /api/playback` | `{"target": "home400", "state": "pause"}`, or no `state` to toggle |
|
||||
| `POST /api/skip` | `{"target": "home400", "direction": "next"}` — `previous` too |
|
||||
| `POST /api/group` | `{"target": "home400", "joined": true}` |
|
||||
| `POST /api/group/none` | every room back on its own |
|
||||
| `GET /api/avr/inputs` | your renamed sources |
|
||||
| `POST /api/avr/input` | `{"code": "GAME"}` |
|
||||
| `GET /api/avr/inputs` | your renamed sources, over HEOS |
|
||||
| `POST /api/avr/input` | `{"code": "inputs/aux_in_1"}` |
|
||||
|
||||
`POST /volume/up` and `/volume/down` take one snapped tap by default; pass
|
||||
`?step=3` and they move that many raw points instead, as they always did.
|
||||
@@ -272,13 +284,12 @@ Used by the interface:
|
||||
The original bridge's endpoints still answer, so existing Shortcuts and
|
||||
scripts keep working: `/targets`, `/volume`, `/volume/{set,up,down,mute}`,
|
||||
`/playback/{play,pause,stop,next,previous}`, `/group/{create,remove}`,
|
||||
`/inputs`, `/input/{set,relay}`, `/avr/{raw,input,inputs}`, `/raw/<command>`.
|
||||
`/inputs`, `/input/{set,relay}`, `/avr/{input,inputs}`, `/raw/<command>`.
|
||||
|
||||
Two of them are worth keeping for troubleshooting:
|
||||
Worth keeping for troubleshooting:
|
||||
|
||||
```
|
||||
GET /raw/browse/browse?sid=1027 # any heos:// command, raw reply
|
||||
GET /avr/raw?cmd=SSFUN ? # any Telnet command, every line back
|
||||
```
|
||||
|
||||
## Working on it
|
||||
@@ -293,11 +304,10 @@ python3 tools/make_icons.py # re-render the icons from static/lo
|
||||
|
||||
```
|
||||
app.py Flask: the UI, the API, and the old bridge's routes
|
||||
controller.py what a room is, what grouping means, volume
|
||||
controller.py what a room is, what grouping means, volume, the AVR's inputs
|
||||
heos.py HEOS CLI client (persistent socket, reconnects itself)
|
||||
avr.py Denon Telnet client + the renamed input list
|
||||
config.py your devices and preferences
|
||||
demo.py fake speakers for --demo
|
||||
templates/ static/ the interface
|
||||
tests/ fake HEOS + AVR servers, and tests against them
|
||||
tests/ a fake HEOS server, and tests against it
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user