L’idea di questo script nasce da un disastro vissuto sulla mia pelle: ero entrato in azienda da qualche settimana quando si è guastata la matrice video di uno studio.
Ok avevo il ricambio in magazzino, ma non avevo idea delle esigenze della sala e quindi ho impiegato una giornata intera a dedurre quali incroci servivano per ripristinare il funzionamento di tutto ciò che ci confluiva.
Risolta la problematica ho poi cercato una soluzione per fare un backup di tutte le altre matrici da conservare per il futuro e mi sono stupito che non esista una soluzione ufficiale integrata nel software fornito da Blackmagic, che stranamente permette di salvare solo le etichette dei vari ingressi ed uscite ma non gli incroci.
Ho quindi cercato un po’ di documentazione online ed ho scoperto che il protocollo di controllo via rete è abbastanza semplice: instaurando una comunicazione TCP sulla porta 9990 la matrice risponde con una serie di informazioni quali modello, versione, nome, etc… dopodiché invia l’elenco delle etichette degli ingressi e delle uscite, seguite dalla configurazione del routing.
PROTOCOL PREAMBLE:
Version: 2.8
VIDEOHUB DEVICE:
Device present: true
Model name: Blackmagic Smart Videohub 40 x 40
Friendly name: Studio 1
Unique ID: 7C2E0DA55E4A
Video inputs: 40
Video processing units: 0
Video outputs: 40
Video monitoring outputs: 0
Serial ports: 0
INPUT LABELS:
0 Camera 1
1 Camera 2
2 Teranex 1
...
OUTPUT LABELS:
0 Monitor 1
1 Monitor 2
2 Hyperdeck 1
...
VIDEO OUTPUT LOCKS:
0 U
1 U
2 U
...
VIDEO OUTPUT ROUTING:
0 35
1 4
2 0
...
CONFIGURATION:
Take Mode: true
END PRELUDE:
Rimanendo connessi si riceverebbe anche eventuali cambiamenti di stato, ma non approfondirò in questo articolo.
Allo stesso modo inviando comandi coerenti con la sintassi descritta è possibile variare la configurazione dei vari ingressi / uscite, per cui è semplice salvare una configurazione in un file di testo e successivamente inviarla per ripristinare un salvataggio.

Con poche righe di Python il processo può essere reso user friendly con una spartana interfaccia Tkinter, ecco il codice completo:
import socket
import tkinter as tk
from tkinter import filedialog, messagebox
def get_videohub_status(ip):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 9990))
data = ""
while True:
chunk = s.recv(4096).decode("utf-8", errors="ignore")
if not chunk:
break
data += chunk
if "END PRELUDE:" in data:
break
s.close()
return data
def send_to_videohub(ip, payload):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, 9990))
s.sendall(payload.encode("utf-8"))
s.close()
def parse_sections(raw):
sections = {}
current = None
for line in raw.splitlines():
if line.endswith(":"):
current = line
sections[current] = []
continue
if current and line.strip():
sections[current].append(line)
return sections
def save_config():
ip = ip_entry.get().strip()
if not ip:
messagebox.showerror("Errore", "Inserisci l'IP della Videohub")
return
try:
raw = get_videohub_status(ip)
except Exception as e:
messagebox.showerror("Errore di connessione", str(e))
return
filename = f"{ip.replace('.', '-')}.txt"
with open(filename, "w", encoding="utf-8") as f:
f.write(raw)
messagebox.showinfo("OK", f"Configurazione salvata in {filename}")
def load_config():
global loaded_config
path = filedialog.askopenfilename(
filetypes=[("Videohub backup", "*.txt")]
)
if not path:
return
with open(path, "r", encoding="utf-8") as f:
loaded_config = f.read()
messagebox.showinfo("OK", "Configurazione caricata in memoria")
def send_config():
ip = ip_entry.get().strip()
if not ip:
messagebox.showerror("Errore", "Inserisci l'IP della Videohub")
return
if not loaded_config:
messagebox.showerror("Errore", "Nessuna configurazione caricata")
return
sections = parse_sections(loaded_config)
payload = ""
for key in (
"INPUT LABELS:",
"OUTPUT LABELS:",
"VIDEO OUTPUT ROUTING:",
):
if key in sections:
payload += key + "\n"
for line in sections[key]:
payload += line + "\n"
payload += "\n"
try:
send_to_videohub(ip, payload)
messagebox.showinfo("OK", "Configurazione inviata alla Videohub")
except Exception as e:
messagebox.showerror("Errore di invio", str(e))
loaded_config = ""
root = tk.Tk()
root.title("Videohub")
tk.Label(root, text="IP Videohub:").grid(row=0, column=0, padx=10, pady=10)
ip_entry = tk.Entry(root, width=20)
ip_entry.grid(row=0, column=1, padx=10, pady=10)
tk.Button(root, text="Salva configurazione", width=20, command=save_config).grid(row=1, column=0, columnspan=2, padx=10, pady=10)
tk.Button(root, text="Carica salvataggio", width=20, command=load_config).grid(row=2, column=0, columnspan=2, padx=10, pady=0)
tk.Button(root, text="Invia alla Videohub", width=20, command=send_config).grid(row=3, column=0, columnspan=2, padx=10, pady=10)
root.mainloop()
