156 lines
5.2 KiB
Python
156 lines
5.2 KiB
Python
import yaml
|
|
import paho.mqtt.client as mqtt # type: ignore
|
|
from paho.mqtt.client import Client # type: ignore
|
|
import jack # type: ignore
|
|
import time
|
|
|
|
client_jack = jack.Client("router")
|
|
|
|
with open("config.yaml", "r") as f:
|
|
config = yaml.safe_load(f)
|
|
|
|
zones = config.get("zones", {})
|
|
sources = config.get("sources", {})
|
|
DEFAULT_SOURCE = "Worship"
|
|
|
|
print("Zones loaded:", zones.keys())
|
|
print("Sources loaded:", sources.keys())
|
|
|
|
|
|
def fade_transition(source_left, source_right, zone_left, zone_right):
|
|
# Disconnect all existing connections to zone inputs
|
|
for dest_port in (zone_left, zone_right):
|
|
for conn in client_jack.get_all_connections(dest_port):
|
|
client_jack.disconnect(conn.name, dest_port)
|
|
print(f"Disconnected {conn.name} → {dest_port}")
|
|
|
|
# Connect new source to zone inputs
|
|
for src_port, dest_port in zip((source_left, source_right), (zone_left, zone_right)):
|
|
client_jack.connect(src_port, dest_port)
|
|
print(f"Connected {src_port} → {dest_port}")
|
|
|
|
def wait_for_available_zone_ports():
|
|
print("Checking which zone destination ports are currently active...")
|
|
active_zones = {}
|
|
|
|
for zone_name, zone_ports in zones.items():
|
|
try:
|
|
client_jack.get_port_by_name(zone_ports["left"])
|
|
client_jack.get_port_by_name(zone_ports["right"])
|
|
active_zones[zone_name] = zone_ports
|
|
print(f"✔️ Zone '{zone_name}' ports are active.")
|
|
except jack.JackError:
|
|
print(f"⚠️ Skipping zone '{zone_name}' — ports not available yet.")
|
|
|
|
return active_zones
|
|
|
|
def apply_default_connections(active_zones):
|
|
for zone_name, zone_ports in active_zones.items():
|
|
if DEFAULT_SOURCE not in sources:
|
|
print(f"Default source '{DEFAULT_SOURCE}' not found. Skipping.")
|
|
continue
|
|
|
|
src_l = sources[DEFAULT_SOURCE]["left"]
|
|
src_r = sources[DEFAULT_SOURCE]["right"]
|
|
dst_l = zone_ports["left"]
|
|
dst_r = zone_ports["right"]
|
|
|
|
try:
|
|
existing = client_jack.get_all_connections(dst_l)
|
|
if not existing:
|
|
fade_transition(src_l, src_r, dst_l, dst_r)
|
|
print(f"Default connection: {DEFAULT_SOURCE} → {zone_name}")
|
|
else:
|
|
print(f"{zone_name} already connected — skipping default.")
|
|
except jack.JackError as e:
|
|
print(f"Error checking/connecting {zone_name}: {e}")
|
|
|
|
|
|
def on_connect(client, userdata, flags, rc):
|
|
print("Connected with result code", rc)
|
|
for zone in zones:
|
|
topic = f"music/{zone}"
|
|
client.subscribe(topic)
|
|
print(f"subscribed to {topic}")
|
|
|
|
def get_required_jack_ports():
|
|
required = []
|
|
|
|
# From sources
|
|
for source_name, source_data in sources.items():
|
|
if source_data.get("active", True):
|
|
required.append(source_data["left"])
|
|
required.append(source_data["right"])
|
|
|
|
# From zones
|
|
for zone_name, zone_data in zones.items():
|
|
if zone_data.get("active", True):
|
|
required.append(zone_data["left"])
|
|
required.append(zone_data["right"])
|
|
|
|
return required
|
|
|
|
def wait_for_required_jack_ports(required_ports, interval=5):
|
|
print("[router] Waiting for all required JACK ports to appear...")
|
|
while True:
|
|
available = set(p.name for p in client_jack.get_ports())
|
|
missing = [p for p in required_ports if p not in available]
|
|
if not missing:
|
|
print("[router] All required JACK ports found. Proceeding.")
|
|
return
|
|
print(f"[router] Still waiting for: {missing}")
|
|
time.sleep(interval)
|
|
|
|
|
|
required_ports = get_required_jack_ports()
|
|
wait_for_required_jack_ports(required_ports)
|
|
|
|
active_zones = wait_for_available_zone_ports()
|
|
apply_default_connections(active_zones)
|
|
|
|
def on_message(client, userdata, msg):
|
|
zone = msg.topic.split("/")[-1]
|
|
source = msg.payload.decode()
|
|
print(f"Message received: Zone={zone}, Source={source}")
|
|
|
|
if zone not in zones:
|
|
print(f"❌ Invalid zone: {zone}")
|
|
return
|
|
if source not in sources:
|
|
print(f"❌ Invalid source: {source}")
|
|
return
|
|
|
|
try:
|
|
zone_left = zones[zone]["left"]
|
|
zone_right = zones[zone]["right"]
|
|
source_left = sources[source]["left"]
|
|
source_right = sources[source]["right"]
|
|
|
|
# Check if source ports exist
|
|
client_jack.get_port_by_name(source_left)
|
|
client_jack.get_port_by_name(source_right)
|
|
|
|
# Check current connection on zone left
|
|
current = None
|
|
conns = client_jack.get_all_connections(zone_left)
|
|
if conns:
|
|
current = conns[0].name.split(":")[0]
|
|
|
|
if current != source_left:
|
|
print(f"🔄 Switching {zone} from {current} to {source}")
|
|
fade_transition(source_left, source_right, zone_left, zone_right)
|
|
else:
|
|
print(f"✅ {zone} already connected to {source}")
|
|
|
|
except jack.JackError as e:
|
|
print(f"⚠️ JACK error for zone '{zone}' or source '{source}': {e}")
|
|
print("⏩ Skipping transition.")
|
|
|
|
|
|
|
|
client = mqtt.Client(protocol=mqtt.MQTTv311)
|
|
client.on_connect = on_connect
|
|
client.on_message = on_message
|
|
|
|
client.connect("10.40.0.2", 1883)
|
|
client.loop_forever() |