Files
Automixbot/config.py
T
2026-04-20 20:17:46 +02:00

177 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import socket
import ipaddress
from pathlib import Path
from typing import Optional
import json
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
# ==========================================
# Specifiche Yamaha TF5 (FONDAMENTALI PER LA CACHE E IL CONTROLLER)
# ==========================================
DEFAULT_PORT = 49280
TF5_INPUT_CHANNELS = 40 # CH 1-40
TF5_ST_INPUT_CHANNELS = 2 # ST IN 1-2
TF5_FX_RETURN_CHANNELS = 4 # FX RTN 1-4
TF5_DCA_GROUPS = 8 # DCA 1-8
TF5_MIX_BUSSES = 20 # MIX 1-20
TF5_MATRIX_BUSSES = 4 # MATRIX 1-4
TF5_STEREO_BUSSES = 1 # 1 Bus Stereo (L/R)
TF5_MUTE_GROUPS = 8 # Mute Master 1-8
TF5_FX_SLOTS = 8 # Processori FX (8 slot interni)
# ==========================================
# Configurazione Cache (Mixer State & IP)
# ==========================================
CACHE_DIR = Path(__file__).parent / "cache"
CACHE_FILE = CACHE_DIR / "tf5_cache.json"
IP_CACHE_FILE = CACHE_DIR / "mixer_ip.json"
CACHE_DURATION = 60.0 # Secondi per la validità dello stato del mixer
IP_CACHE_DURATION = 300 # 5 minuti per la validità dell'IP del mixer
# Assicurati che la cartella esista
CACHE_DIR.mkdir(parents=True, exist_ok=True)
# ==========================================
# Funzioni di Utilità per Auto-Discovery
# ==========================================
def log(message: str, level: str = "INFO"):
"""Stampa log formattato con timestamp"""
timestamp = time.strftime("%H:%M:%S")
symbols = {
"INFO": "️", "SUCCESS": "✅", "ERROR": "❌",
"WARNING": "⚠️", "SEARCH": "🔍", "NETWORK": "🌐",
"CACHE": "📍", "TIME": "⏱️"
}
symbol = symbols.get(level, "•")
print(f"[{timestamp}] {symbol} {message}")
def get_local_network() -> str:
"""Ottiene la rete locale del sistema"""
log("Rilevamento rete locale in corso...", "NETWORK")
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
local_ip = s.getsockname()[0]
s.close()
ip_parts = local_ip.split('.')
network = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.0/24"
log(f"IP locale rilevato: {local_ip}", "INFO")
log(f"Rete da scansionare: {network}", "NETWORK")
return network
except Exception as e:
log(f"Errore nel rilevare la rete locale: {e}", "ERROR")
network = "192.168.1.0/24"
log(f"Uso rete di fallback: {network}", "WARNING")
return network
def check_port(ip: str, port: int, timeout: float = 0.5) -> bool:
"""Controlla se una porta è aperta su un IP"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((ip, port))
sock.close()
return result == 0
except Exception:
return False
def scan_network_for_mixer(port: int = DEFAULT_PORT, max_workers: int = 50) -> Optional[str]:
"""Scansiona la rete locale per trovare il mixer"""
log(f"Ricerca mixer sulla porta {port}...", "SEARCH")
start_time = time.time()
network = get_local_network()
ip_network = ipaddress.ip_network(network, strict=False)
total_hosts = sum(1 for _ in ip_network.hosts())
log(f"Indirizzi da scansionare: {total_hosts} (Thread: {max_workers})", "INFO")
found_ip = None
scanned = 0
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_to_ip = {executor.submit(check_port, str(ip), port): str(ip) for ip in ip_network.hosts()}
for future in as_completed(future_to_ip):
ip = future_to_ip[future]
scanned += 1
if scanned % 50 == 0:
log(f"Progresso: {scanned}/{total_hosts} IP", "INFO")
try:
if future.result():
found_ip = ip
elapsed = time.time() - start_time
log(f"MIXER TROVATO su {ip}:{port}", "SUCCESS")
log(f"Tempo di scansione: {elapsed:.2f}s", "TIME")
executor.shutdown(wait=False, cancel_futures=True)
break
except Exception:
pass
if not found_ip:
log(f"Nessun mixer trovato. Tempo: {time.time() - start_time:.2f}s", "ERROR")
return found_ip
def load_cached_ip() -> Optional[str]:
"""Carica l'IP dalla cache se valido"""
if not IP_CACHE_FILE.exists():
return None
try:
with open(IP_CACHE_FILE, 'r') as f:
data = json.load(f)
cached_ip = data.get('ip')
age = time.time() - data.get('timestamp', 0)
if age < IP_CACHE_DURATION:
log(f"Verifica raggiungibilità IP in cache: {cached_ip}...", "CACHE")
if cached_ip and check_port(cached_ip, DEFAULT_PORT, timeout=1.0):
log(f"IP dalla cache valido: {cached_ip}", "SUCCESS")
return cached_ip
else:
log(f"IP dalla cache non raggiungibile.", "WARNING")
else:
log(f"Cache IP scaduta.", "WARNING")
except Exception as e:
log(f"Errore lettura cache IP: {e}", "ERROR")
return None
def save_ip_to_cache(ip: str):
"""Salva l'IP nella cache"""
try:
with open(IP_CACHE_FILE, 'w') as f:
json.dump({'ip': ip, 'timestamp': time.time()}, f, indent=2)
except Exception as e:
log(f"Errore nel salvare cache IP: {e}", "ERROR")
def get_mixer_ip() -> str:
"""Ottiene l'IP del mixer (Cache -> Discovery -> Fallback)"""
log("=== AVVIO RICERCA YAMAHA TF5 ===", "INFO")
cached_ip = load_cached_ip()
if cached_ip:
return cached_ip
discovered_ip = scan_network_for_mixer()
if discovered_ip:
save_ip_to_cache(discovered_ip)
return discovered_ip
fallback_ip = "192.168.1.57"
log(f"Uso IP predefinito di fallback: {fallback_ip}", "WARNING")
return fallback_ip
# ==========================================
# ESECUZIONE AL CARICAMENTO DEL MODULO
# ==========================================
# Viene eseguito nel momento in cui mixer_controller.py fa "from config import *"
DEFAULT_HOST = get_mixer_ip()