← Back to list

Da un Semplice Script a un Bridge MQTT: Anatomia di un’Odissea di Debug con Python e Kasa

Come il tentativo di controllare delle valvole termostatiche si è trasformato in una profonda esplorazione di ambienti virtuali…

Il Prof. in Automatizzo con il codice · 2025-08-17 08:31 · 0 claps · 4.1 min read
#automation #kasa #mqtt #python #domotica
Open on Medium ↗
Wiki topics: FT · Fine-tuning & Adaptation

Da un Semplice Script a un Bridge MQTT: Anatomia di un’Odissea di Debug con Python e Kasa

Come il tentativo di controllare delle valvole termostatiche si è trasformato in una profonda esplorazione di ambienti virtuali, programmazione asincrona e design di API.

ai generated

ai generated

Nel mondo della domotica, spesso le idee più semplici nascondono le sfide più complesse. La mia era una di queste: integrare le mie valvole termostatiche TP-Link Kasa (KE100), gestite da un hub (KH100), nel mio ecosistema MQTT esistente. L’obiettivo era ottenere un controllo locale, robusto e indipendente dal cloud.

Esisteva una promettente libreria open-source, python-kasa, che sembrava perfetta per lo scopo. Pensavo sarebbe stato un progetto da un fine settimana. Si è rivelato un’immersione profonda e, a tratti, frustrante, nelle complessità del moderno sviluppo in Python. Questa è la cronaca di quel viaggio, un’analisi degli ostacoli incontrati e delle soluzioni che, alla fine, hanno dato vita a un bridge software di cui sono veramente orgoglioso.

L’Ostacolo #1: L’Inizializzazione del Progetto e la Giungla degli Ambienti

Ogni progetto Python inizia con una domanda fondamentale: come gestisco le dipendenze? La mia scelta iniziale, Poetry, si è scontrata con un pyproject.toml configurato per un altro build system, Hatch. Questo ha portato alla prima grande lezione: un pyproject.toml non implica necessariamente l’uso di Poetry. Controllare la sezione [build-system] è un passo cruciale per evitare false partenze.

Decidendo di tornare alle basi, ho optato per un ambiente virtuale standard (venv). Qui, però, si è manifestato un problema ancora più subdolo: un conflitto di nomi. Un’altra libreria, chiamata anch’essa kasa, era installata nel mio ambiente Python globale. Anche con il venv attivo, un uso errato del comando (python3 invece di python su Windows) faceva sì che venisse importata la libreria sbagliata, generando un ImportError apparentemente illogico.

Soluzione Definitiva: Isolare completamente il progetto in una sua cartella, usare un venv pulito, e installare le dipendenze con pip install -r requirements.txt. È l’approccio più semplice e a prova di errore, specialmente quando si sospetta una contaminazione dell’ambiente globale.

L’Ostacolo #2: Le Sottigliezze della Programmazione Asincrona

Una volta stabilita la comunicazione, il bridge doveva eseguire due compiti in parallelo: interrogare periodicamente lo stato delle valvole (polling) e rimanere in ascolto di comandi da MQTT. La scelta naturale era asyncio.

Il primo prototipo utilizzava la popolare libreria paho-mqtt, che avvia il suo loop di rete in un thread separato. Il risultato è stato un comportamento inspiegabile: il ciclo di polling asyncio si bloccava senza errori. Questo è un classico sintomo di un conflitto tra il loop di eventi di asyncio e un loop basato su thread.

Soluzione Definitiva: Riscrivere la parte MQTT usando una libreria nativamente asincrona. La scelta è ricaduta su aiomqtt, che si integra perfettamente nel singolo loop di eventi di asyncio. Questo ha risolto i blocchi e ha reso il codice più pulito e coerente, gestendo tutte le operazioni di I/O all’interno di un unico paradigma. Su Windows, è stato inoltre necessario impostare esplicitamente una diversa event loop policy (WindowsSelectorEventLoopPolicy) per garantire la compatibilità, un “rito di passaggio” per molti sviluppatori di rete su questa piattaforma.

ai generated

ai generated

L’Ostacolo #3: Navigare un’API in Sviluppo

Lavorare con un progetto open-source significa a volte usare il codice direttamente dal repository Git per avere le ultime funzionalità. Questo, però, espone a un’API che può cambiare. Ci siamo scontrati con diversi di questi cambiamenti:

  1. Nomi di Classi Mutevoli: La classe per gestire l’hub, che in versioni precedenti era SmartHub, nella versione di sviluppo era stata rinominata in IotHub e spostata in un sottomodulo.
  2. Proprietà Mancanti: Attributi di convenienza come .is_hub o .is_thermostat, presenti in passato, non esistevano più.
  3. Un Bug di Caching: Dopo l’invio di un comando, una successiva lettura dello stato restituiva i valori vecchi. La libreria non stava invalidando la sua cache interna.

Questi problemi ci hanno insegnato l’importanza del debugging basato sull’introspezione. Invece di fare affidamento sulla documentazione (che potrebbe non essere aggiornata), la soluzione è stata interrogare l’oggetto stesso.

Soluzione Definitiva:

  • Usare l’API Pubblica: Abbiamo smesso di importare classi interne e abbiamo adottato il metodo ufficiale Discover.discover_single(IP, credentials=…), che si è rivelato l’unico punto di ingresso stabile per ottenere un dispositivo autenticato.
  • Controllo Basato sui Dati: Invece di if valvola.is_thermostat:, siamo passati a if “temperature” in valvola.features:. Questo approccio “duck typing” è molto più robusto, perché si basa sulle capacità effettive del dispositivo piuttosto che su un flag astratto.
  • Invalidazione Esplicita: Per il bug di caching, la soluzione è stata forzare un aggiornamento sull’oggetto genitore (await valvola.parent.update()) dopo ogni comando, garantendo che lo stato venisse ricaricato dalla fonte.

L’Evoluzione Finale: Da Statico a Dinamico

Risolti i problemi di connessione e controllo, l’ultimo passo è stato rendere il bridge veramente “smart”.

  1. Scoperta Attiva: Invece di un broadcast UDP (inaffidabile), lo script ora esegue una scansione attiva degli indirizzi IP, un metodo più lento ma immensamente più robusto.
  2. Configurazione Ibrida: Per ottimizzare, il file config.yaml ora permette di definire una lista di “lavori” di scoperta, combinando il broadcast veloce, il controllo di IP specifici e la scansione di intere reti.
  3. Scoperta Ricorrente: La scoperta non avviene solo all’avvio. Un task dedicato la ripete a intervalli regolari, permettendo al bridge di trovare nuovi dispositivi aggiunti alla rete senza bisogno di un riavvio.

Conclusione

Quello che era iniziato come uno script di poche decine di righe si è evoluto in un servizio domotico completo e resiliente. Ogni errore, ogni frustrazione, è stata un’opportunità per imparare e per costruire una soluzione migliore. Questo progetto è la testimonianza che, a volte, la strada più difficile è anche quella che insegna di più.

Se volete esplorare il codice finale o usarlo per i vostri dispositivi, trovate il progetto completo sul mio repository GitHub.

[Link al Repository GitHub]

Spero che questa cronaca possa essere d’aiuto a chiunque si trovi ad affrontare sfide simili. Nella domotica, come nella programmazione, la perseveranza non è solo una virtù, è un requisito fondamentale.


메타데이터
post_id
adfa1bb738ce
slug
da-un-semplice-script-a-un-bridge-mqtt-anatomia-di-unodissea-di-debug-con-python-e-kasa-adfa1bb738ce
url
https://automatizzo.conilcodice.it/da-un-semplice-script-a-un-bridge-mqtt-anatomia-di-unodissea-di-debug-con-python-e-kasa-adfa1bb738ce
canonical_url
https://automatizzo.conilcodice.it/da-un-semplice-script-a-un-bridge-mqtt-anatomia-di-unodissea-di-debug-con-python-e-kasa-adfa1bb738ce
author_url
https://medium.com/@il-prof
status
ok
fetched_at
2026-07-18 04:24:10