now a working radio controller

This commit is contained in:
2026-08-22 14:59:22 +02:00
parent 35969ad305
commit 16a88f8d1f
6 changed files with 334 additions and 7 deletions
+53 -4
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import argparse
import socket
import ssl
import sys
from collections.abc import Callable
@@ -10,6 +11,8 @@ from urllib.error import URLError
from urllib.parse import quote
from urllib.request import Request, urlopen
import lib_heos
_host = "192.168.178.177"
_port = 8080
@@ -54,12 +57,44 @@ def off() -> None:
_command("PWSTANDBY")
def _power_state() -> str:
"""Return ON or STANDBY using Denon's control-protocol status query."""
try:
with socket.create_connection((_host, 23), timeout=_timeout) as connection:
connection.settimeout(_timeout)
connection.sendall(b"PW?\r")
with connection.makefile("r", encoding="ascii", newline="\r") as stream:
while line := stream.readline():
response = line.strip()
if response.startswith("PW"):
state = response[2:]
if state in {"ON", "STANDBY"}:
return state
except OSError as error:
raise RuntimeError(f"Cannot query Denon power state: {error}") from error
raise RuntimeError("Denon did not return a valid power state")
def next() -> None:
"""Select the next preset or source."""
"""Play the next HEOS favorite according to the receiver's power state."""
lib_heos.configure(_host, timeout=_timeout)
if _power_state() == "STANDBY":
lib_heos.play_favorite(1)
return
# Deliberately do nothing when the receiver is on but its current media is
# not one of the configured HEOS favorites.
lib_heos.next_favorite()
def previous() -> None:
"""Select the previous preset or source."""
"""Play the previous HEOS favorite according to the receiver's power state."""
lib_heos.configure(_host, timeout=_timeout)
if _power_state() == "STANDBY":
lib_heos.play_favorite(1)
return
# Deliberately do nothing when the receiver is on but its current media is
# not one of the configured HEOS favorites.
lib_heos.previous_favorite()
def volume_up() -> None:
@@ -74,12 +109,25 @@ def volume_down() -> None:
_command("MVDOWN")
def volume_mute() -> None:
"""Mute the volume."""
_command("MUON")
def main() -> int:
"""Run a receiver action from the command line."""
parser = argparse.ArgumentParser(description="Control the Denon receiver")
parser.add_argument(
"action",
choices=("on", "off", "next", "previous", "volume-up", "volume-down"),
choices=(
"on",
"off",
"next",
"previous",
"volume-up",
"volume-down",
"volume-mute",
),
)
parser.add_argument("--host", default=_host, help=f"receiver address (default: {_host})")
parser.add_argument("--port", type=int, default=_port, help=f"HTTP port (default: {_port})")
@@ -116,10 +164,11 @@ def main() -> int:
"previous": previous,
"volume-up": volume_up,
"volume-down": volume_down,
"volume-mute": volume_mute,
}
try:
actions[args.action]()
except (OSError, URLError) as error:
except (OSError, URLError, RuntimeError, lib_heos.HeosError) as error:
print(f"Denon {args.action} failed: {error}", file=sys.stderr)
return 1
return 0