L’integrazione tra dispositivi di automazione domestica rappresenta oggi un elemento fondamentale per migliorare comfort, efficienza energetica e controllo degli impianti domestici. L’integrazione tra un controllore programmabile come SlimLine ed i termostati Shelly non solo permette di monitorare e regolare in tempo reale la temperatura degli ambienti, ma offre anche la possibilità di implementare logiche di controllo avanzate. Ad esempio, il PLC può decidere automaticamente quali zone riscaldare o raffrescare in base a parametri come orari programmati, presenza in ambiente, valori di temperatura esterna o scenari predefiniti.
Il sistema SlimLine Elsist è un PLC (Programmable Logic Controller) compatto e versatile, progettato per l’automazione di impianti civili e industriali. Grazie alla modularità e alla possibilità di programmare logiche complesse, consente il controllo di sistemi elettrici, termici e meccanici in modo affidabile e personalizzabile. L’ambiente di sviluppo gratuito LogicLab ne permette la programmazione nei 5 linguaggi sia grafici che testuali definiti dalla norma IEC61131.
Il termostato Shelly è un dispositivo smart progettato per il controllo automatico dei sistemi di riscaldamento e raffrescamento all’interno di impianti domotici e tradizionali. Grazie alla connettività integrata e alla compatibilità con numerosi protocolli di automazione, consente una gestione avanzata della temperatura ambiente.
L’integrazione si realizza utilizzando dallo SlimLine chiamate HTTP REST API tramite il FB HTTPClient in accordo al protocollo RPC Protocol di Shelly. Grazie al formato JSON dei dati, la comunicazione tra SlimLine e i termostati Shelly è estremamente flessibile e scalabile. È possibile aggiungere nuovi dispositivi alla rete senza modifiche sostanziali alla logica di controllo, semplificando l’espansione dell’impianto. L’uso di JSONEncoder e JSONDecoder consente al PLC di interpretare facilmente i dati ricevuti dai termostati, elaborare comandi e inviare istruzioni ai dispositivi Shelly in modo sicuro e standardizzato.
Lo scopo di questo articolo è di scendere nel detlaglio di una realizzazione pratica di seguito riportiamo le operazioni da eseguire.

Configurazione termostato
La prima operazione è configurare il termostato Shelly, questo disositivo nelle versioni più recenti (Gen2/Gen3) non ha l’Access Point Wi-Fi (AP mode) tradizionale, quindi non puoi collegarti direttamente al dispositivo tramite hotspot Wi-Fi come avveniva sulle Gen1. Occorre dal display accedere al menù impostazioni, abilitare il WiFi che visualizzerà le reti presenti e connettersi alla rete desiderata impostando la password di accesso.
Connesso alla rete verrà visualizzato l’indirizzo IP assegnato al dispositivo, da un browser è possibile definire l’indirizzo ed accedere alla pagina di configurazione. Per un test del protocollo RPC da browser basta indicare nella barra indirizzo http://IP Shelly/status e ci verrà ritornato un messaggio JSON con lo stato. I messaggi RPC vanno inviati all’indirizzo indirizzo http://IP Shelly/rpc.
Messaggi RPC
| Funzione da eseguire | Richiesta | Risposta |
|---|---|---|
| Lettura temperatura | {“id”:1, “method”:”Temperature.GetStatus”, “params”:{“id”:0}} | {“id”:0,”tC”:26.2,”tF”:79.2} |
| Lettura umidità | {“id”:1, “method”:”Humidity.GetStatus”, “params”:{“id”:0}} | {“id”: 0,”rh”: 43.6} |
| Lettura illuminazione | {“id”:1, “method”:”Illuminance.GetStatus”, “params”:{“id”:0}} | {“id”: 0,”lux”: 200,”illumination”: “bright”} |
| Impostazione set termostato a 22.0 gradi | {“id”: 1,”method”: “Thermostat.SetConfig”,”params”: {“id”: 0,”config”: {“target_C”: 22.0,”enable”: true}}} | {“restart_required”: false} |
| Lettura stato termostato | {“id”: 1,”method”: “Thermostat.GetStatus”,”params”: {“id”: 0}} | {“id”: 0,”enable”: true,”target_C”: 14.5,”current_C”: 22.9,”output”: false,”schedules”: {“enable”: false}} |
Scrittura programma su SlimLine
Ora vediamo come realizzare un semplice programma su di un dispositivo SlimLine per acquisire il valore di temperatura dal termostato. Il programma potrà essere modificato per gestire l’acquisizione di altre variabili e/o impostare il set di temperatura.
LogicLab (Ptp181, ShellyWallDisplay)
PROGRAM ShellyWallDisplay
VAR CONSTANT
BSize : UDINT := 128; (* Buffer size definition *)
END_VAR
VAR
CaseNr : USINT; (* Program case *)
Errors : UDINT; (* Execution errors *)
TimeBf : UDINT; (* Time buffer (mS) *)
Temperature : REAL; (* Temperature °C *)
PStart : PVOID; (* Result start position *)
PEnd : PVOID; (* Result end position *)
Answer : STRING[ 128 ]; (* Page string *)
TCPClient : SysTCPClient; (* TCP client management *)
HTTPRq : HTTPClient_v5; (* HTTP client *)
END_VAR
// *****************************************************************************
// PROGRAM "ShellyWallDisplay"
// *****************************************************************************
// The program reads the current temperature from a Shelly Wall Display.
// -----------------------------------------------------------------------------
// -------------------------------------------------------------------------
// INITIALIZATIONS
// -------------------------------------------------------------------------
// Program initializations.
IF (SysFirstLoop) THEN
// Set TCPClient parameters.
TCPClient.PeerAdd:=ADR('192.168.1.109'); //Peer address
TCPClient.PeerPort:=80; //Peer port
TCPClient.LocalAdd:=ADR('0.0.0.0'); //Local address
TCPClient.LocalPort:=0; //Local port
TCPClient.FlushTm:=50; //Flush time (mS)
TCPClient.LifeTm:=20; //Life time (S)
TCPClient.RxSize:=512; //Rx buffer size
TCPClient.TxSize:=512; //Tx buffer size
// Set HTTPClient parameters.
HTTPRq.SpyOn:=TRUE; //Activate the spy
HTTPRq.KeepAlive:=FALSE; //HTTP keep-alive
HTTPRq.RMethod:=HTTP_REQUEST#HTTP_POST; //HTTP request method
HTTPRq.HostName:=TCPClient.PeerAdd; // Hostname
HTTPRq.Page:=ADR('rpc'); //Web page
HTTPRq.Request:=eNULL; //Request string
HTTPRq.Header:=ADR('Content-Type: application/json$r$nAccept: application/json$r$n'); //HTTP header
HTTPRq.DBSize:=512; //Data buffer size
HTTPRq.Timeout:=T#10s; //Execution timeout
// Set local variables.
TimeBf:=SysTimeGetMs(); //Time buffer (mS)
END_IF;
// -------------------------------------------------------------------------
// MANAGE CONNECTION
// -------------------------------------------------------------------------
// Manage the TCP/HTTP connection.
TCPClient(Connect:=HTTPRq.Connect); //Manage TCP client
HTTPRq(Enable:=TRUE, File:=TCPClient.File); //Enable HTTP client
IF (HTTPRq.Fault) THEN CaseNr:=0; END_IF;
// -------------------------------------------------------------------------
// PROGRAM SEQUENCES
// -------------------------------------------------------------------------
// Program sequences.
CASE (CaseNr) OF
// ---------------------------------------------------------------------
// Initialize timer for acquisition delay.
0:
HTTPRq.Send:=FALSE; //Do not send request yet
TimeBf:=SysTimeGetMs(); //Time buffer (mS)
IF (HTTPRq.Request <> eNULL) THEN eTO_JUNK(SysRMFree(ADR(HTTPRq.Request))); END_IF;
CaseNr:=CaseNr+1; //Move to next case
// ---------------------------------------------------------------------
// Wait for delay, then allocate buffers and enable client.
1:
IF ((SysTimeGetMs()-TimeBf) < TO_UDINT(T#10s)) THEN RETURN; END_IF;
IF NOT(SysRMAlloc(BSize, ADR(HTTPRq.Request))) THEN RETURN; END_IF;
CaseNr:=CaseNr+1; //Program case
// ---------------------------------------------------------------------
// Create an RPC request to read the temperature and send it.
// {"id":1, "method":"Temperature.GetStatus", "params":{"id":0}}
2:
eTO_JUNK(SysVsnprintf(HTTPRq.Request, BSize, ADR('%s'), STRING_TYPE, ADR('{"id":1, "method":"Temperature.GetStatus", "params":{"id":0}}')));
HTTPRq.Send:=TRUE; //Send request
CaseNr:=CaseNr+1; //Move to next case
// ---------------------------------------------------------------------
// Waiting for the response.
//{"id":1,"src":"ShellyWallDisplay-0008220BB6BE","result":{"id":0,"tC":23,"tF":73.4}}
3:
IF ((HTTPRq.DBChars <> 0) AND HTTPRq.HPSelector) THEN
IF ((Sysstrlen(ADR(Answer))+HTTPRq.DBChars) < SIZEOF(Answer)) THEN
eTO_JUNK(Sysmemmove(eTO_POINTER(ADR(Answer))+Sysstrlen(ADR(Answer)), HTTPRq.DBAddress, HTTPRq.DBChars));
END_IF;
END_IF;
// On completion, read the temperature.
IF (HTTPRq.Done) THEN
CaseNr:=0; //Reset program case
IF NOT(HTTPRq.PLoad) THEN Errors:=Errors+1; RETURN; END_IF;
// Find the "result" JSON from the response.
PStart:=SysStrFind(ADR(Answer), ADR('"result":'), FIND_GET_END); //Start position of "result"
PEnd:=SysStrFind(PStart, ADR('}'), FIND_DEFAULT); //End position of "result"
eTO_JUNK(Sysmemmove(ADR(Answer), PStart, TO_UDINT(PEnd-PStart)));
IF (JSONDecoder(ADR(Answer), ADR('tC'), REAL_TYPE, ADR(Temperature), 1, 0) <> 0) THEN Errors:=Errors+1; END_IF;
END_IF;
END_CASE;
// [End of file]