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}") time.sleep(1) 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()