Audio Router
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import signal
|
||||
import time
|
||||
|
||||
import jack
|
||||
import yaml
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
|
||||
CONFIG_FILE = "config.yaml"
|
||||
|
||||
|
||||
class JackRouter:
|
||||
def __init__(self):
|
||||
with open(CONFIG_FILE, "r") as f:
|
||||
self.cfg = yaml.safe_load(f)
|
||||
|
||||
self.client = jack.Client("AudioRouter")
|
||||
self.mode = "mute_all"
|
||||
self.base_topic = self.cfg["mqtt"]["base_topic"].rstrip("/")
|
||||
|
||||
def connect(self, src, dst):
|
||||
try:
|
||||
if not self.client.get_all_connections(dst).count(src):
|
||||
self.client.connect(src, dst)
|
||||
print(f"Connected: {src} -> {dst}")
|
||||
except jack.JackError as e:
|
||||
print(f"Connect failed: {src} -> {dst}: {e}")
|
||||
|
||||
def disconnect(self, src, dst):
|
||||
try:
|
||||
self.client.disconnect(src, dst)
|
||||
print(f"Disconnected: {src} -> {dst}")
|
||||
except jack.JackError:
|
||||
pass
|
||||
|
||||
def clear_routes(self):
|
||||
all_routes = self.cfg["routes"]
|
||||
|
||||
for route in all_routes.values():
|
||||
source = self.cfg["ports"][route["source"]]
|
||||
output = self.cfg["ports"][route["output"]]
|
||||
|
||||
pairs = [
|
||||
(source["left"], output["left"]),
|
||||
(source["right"], output["right"]),
|
||||
]
|
||||
|
||||
for src, dst in pairs:
|
||||
self.disconnect(src, dst)
|
||||
|
||||
def routes_for_mode(self, mode):
|
||||
preset = self.cfg["presets"].get(mode)
|
||||
|
||||
if preset is None:
|
||||
return None
|
||||
|
||||
resolved_routes = []
|
||||
|
||||
for route_name, enabled in preset.items():
|
||||
if not enabled:
|
||||
continue
|
||||
|
||||
route = self.cfg["routes"][route_name]
|
||||
|
||||
source_name = route["source"]
|
||||
output_name = route["output"]
|
||||
|
||||
source = self.cfg["ports"][source_name]
|
||||
output = self.cfg["ports"][output_name]
|
||||
|
||||
resolved_routes.append((source["left"], output["left"]))
|
||||
resolved_routes.append((source["right"], output["right"]))
|
||||
|
||||
return resolved_routes
|
||||
|
||||
def set_mode(self, mode):
|
||||
routes = self.routes_for_mode(mode)
|
||||
if routes is None:
|
||||
print(f"Unknown mode: {mode}")
|
||||
return False
|
||||
|
||||
print(f"Setting mode: {mode}")
|
||||
self.clear_routes()
|
||||
|
||||
for src, dst in routes:
|
||||
self.connect(src, dst)
|
||||
|
||||
self.mode = mode
|
||||
return True
|
||||
|
||||
def state(self):
|
||||
return {
|
||||
"mode": self.mode,
|
||||
"running": True,
|
||||
"timestamp": int(time.time()),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
router = JackRouter()
|
||||
|
||||
mqtt_cfg = router.cfg["mqtt"]
|
||||
client = mqtt.Client()
|
||||
|
||||
def publish_state():
|
||||
client.publish(f"{router.base_topic}/state", json.dumps(router.state()), retain=True)
|
||||
client.publish(f"{router.base_topic}/mode/state", router.mode, retain=True)
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
print(f"MQTT connected: {rc}")
|
||||
client.subscribe(f"{router.base_topic}/mode/set")
|
||||
router.set_mode("aux_only")
|
||||
publish_state()
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
mode = msg.payload.decode().strip()
|
||||
print(f"MQTT received: {msg.topic} = {mode}")
|
||||
|
||||
if router.set_mode(mode):
|
||||
publish_state()
|
||||
|
||||
def shutdown(*args):
|
||||
print("Stopping router...")
|
||||
router.set_mode("mute_all")
|
||||
publish_state()
|
||||
raise SystemExit
|
||||
|
||||
signal.signal(signal.SIGINT, shutdown)
|
||||
signal.signal(signal.SIGTERM, shutdown)
|
||||
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
|
||||
with router.client:
|
||||
client.connect(mqtt_cfg["host"], int(mqtt_cfg["port"]), 60)
|
||||
client.loop_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user