added marantz watcher script
This commit is contained in:
parent
1dabffeec9
commit
b42d75dca8
140
scripts/marantz-watcher.py
Normal file
140
scripts/marantz-watcher.py
Normal file
@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
# THIS IS AI-GENERATED, USE AT YOUR OWN RISK
|
||||
# Simple script to turn on, set volume and source when Plexamp starts playing using Marantz API. Tested on a Marantz M-CR510.
|
||||
# To be run on same machine that outputs audio (where Plexamp Headless is installed)
|
||||
import glob
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import requests
|
||||
|
||||
# ================= CONFIG =================
|
||||
RECEIVER_IP = "10.X.X.X"
|
||||
INPUT_PLAYING = "SIAUXD"
|
||||
INPUT_FALLBACK = "SISERVER" # optional fallback input when music stops
|
||||
FALLBACK_BEHAVIOR = "nothing" # "standby" or "input" or "nothing"
|
||||
CHECK_INTERVAL = 5 # seconds between ALSA checks
|
||||
POWER_ON_TIMEOUT = 5 # seconds for power-on request
|
||||
SOURCE_SWITCH_DELAY = 10 # seconds to wait after power-on before switching input
|
||||
# Denon volume range
|
||||
VOL_MIN_DB = -79.5
|
||||
VOL_MAX_DB = 18.0
|
||||
|
||||
|
||||
# ==========================================
|
||||
|
||||
def parse_status(file_path):
|
||||
"""Parse ALSA PCM state."""
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
content = f.read()
|
||||
if "RUNNING" in content:
|
||||
return "playing"
|
||||
elif "PREPARED" in content:
|
||||
return "paused"
|
||||
elif "closed" in content:
|
||||
return "off"
|
||||
except Exception:
|
||||
return None
|
||||
return None
|
||||
|
||||
|
||||
def get_audio_state():
|
||||
"""Return overall audio state based on ALSA status."""
|
||||
states = []
|
||||
for f in glob.glob("/proc/asound/card*/pcm*/sub*/status"):
|
||||
state = parse_status(f)
|
||||
if state:
|
||||
states.append(state)
|
||||
if "playing" in states:
|
||||
return "playing"
|
||||
if "paused" in states:
|
||||
return "paused"
|
||||
return "off"
|
||||
|
||||
|
||||
def is_receiver_on():
|
||||
"""Check if Marantz is powered on via /formMainZone_MainZoneXml.xml."""
|
||||
try:
|
||||
r = requests.get(f"http://{RECEIVER_IP}/goform/formMainZone_MainZoneXml.xml", timeout=5)
|
||||
r.raise_for_status()
|
||||
root = ET.fromstring(r.text)
|
||||
power_node = root.find(".//Power/value")
|
||||
if power_node is not None:
|
||||
power_state = power_node.text.strip().upper()
|
||||
print(f"Receiver power state: {power_state}")
|
||||
return power_state != "STANDBY"
|
||||
except Exception as e:
|
||||
print("⚠️ Error checking receiver power:", e)
|
||||
return False
|
||||
|
||||
|
||||
def set_volume(decimal_volume: float):
|
||||
"""
|
||||
Set Denon volume.
|
||||
decimal_volume: 0.0 = min, 1.0 = max
|
||||
"""
|
||||
decimal_volume = max(0.0, min(1.0, decimal_volume))
|
||||
denon_db = decimal_volume * (VOL_MAX_DB - VOL_MIN_DB) + VOL_MIN_DB
|
||||
try:
|
||||
print(f"http://{RECEIVER_IP}/goform/formiPhoneAppVolume.xml?1+{denon_db:.0f}")
|
||||
requests.get(f"http://{RECEIVER_IP}/goform/formiPhoneAppVolume.xml?1+{denon_db:.0f}", timeout=5)
|
||||
print(f"➡️ Volume set to {denon_db:.2f} dB ({decimal_volume * 100:.0f}%)")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print("⚠️ Error setting volume:", e)
|
||||
|
||||
|
||||
def marantz_power_on():
|
||||
if not is_receiver_on():
|
||||
try:
|
||||
requests.get(f"http://{RECEIVER_IP}/goform/formiPhoneAppPower.xml?1+PowerOn", timeout=POWER_ON_TIMEOUT)
|
||||
print("➡️ Power on request sent")
|
||||
time.sleep(SOURCE_SWITCH_DELAY)
|
||||
except requests.exceptions.RequestException as e:
|
||||
print("⚠️ Error powering on:", e)
|
||||
# Always switch input after ensuring receiver is on
|
||||
try:
|
||||
requests.get(f"http://{RECEIVER_IP}/goform/formiPhoneAppInputSelect.xml?{INPUT_PLAYING}", timeout=5)
|
||||
print(f"➡️ Input switched to {INPUT_PLAYING}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print("⚠️ Error switching input:", e)
|
||||
|
||||
|
||||
def marantz_power_off():
|
||||
try:
|
||||
requests.get(f"http://{RECEIVER_IP}/goform/formiPhoneAppPower.xml?1+PowerStandby", timeout=5)
|
||||
print("➡️ Power standby request sent")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print("⚠️ Error sending power off:", e)
|
||||
|
||||
|
||||
def marantz_fallback_input():
|
||||
try:
|
||||
requests.get(f"http://{RECEIVER_IP}/goform/formiPhoneAppInputSelect.xml?{INPUT_FALLBACK}", timeout=5)
|
||||
print(f"➡️ Input switched to fallback {INPUT_FALLBACK}")
|
||||
except requests.exceptions.RequestException as e:
|
||||
print("⚠️ Error switching to fallback input:", e)
|
||||
|
||||
|
||||
def main():
|
||||
last_state = None
|
||||
while True:
|
||||
state = get_audio_state()
|
||||
if state != last_state:
|
||||
print(f"🔄 State changed: {last_state} -> {state}")
|
||||
if state == "playing":
|
||||
marantz_power_on()
|
||||
set_volume(0.35)
|
||||
elif state == "off":
|
||||
set_volume(0.1)
|
||||
if FALLBACK_BEHAVIOR == "standby":
|
||||
marantz_power_off()
|
||||
elif FALLBACK_BEHAVIOR == "input":
|
||||
marantz_fallback_input()
|
||||
# paused -> do nothing
|
||||
last_state = state
|
||||
time.sleep(CHECK_INTERVAL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
x
Reference in New Issue
Block a user