OpenTherm în NodeRed

Acest tutorial este transpunerea practică a celor relatate în articolul "Economie de gaz cu OpenTherm".
Se presupune că am configurat modulul OpenTherm iar calea in Tasmota este: %topic%/%prefix%/ adica 'ot/...'.



Vom creea o logică de comandă pentru centrala OpenTherm după cum urmează:


Datele care ne interesează le preluăm din 'ot/tele/SENSOR' (mesaj JSON) cu un nod MQTT in.


Datele obținute sunt prelucrate în nodul funcție denumit 'work' astfel:

var B = msg.payload.OPENTHERM.BTMP.REQ;
var W = msg.payload.OPENTHERM.HWTMP.REQ;
var CH = msg.payload.OPENTHERM.SLAVE.CH;
var DHW = msg.payload.OPENTHERM.SLAVE.DHW;

var acm_low = temperatura(0); // temperatura ACM pentru reducere consum
var acm_high = temperatura(1); // temperatura ACM pentru baie
var ch_low = temperatura(2); // temperatura tur pentru mentinere
var ch_high = temperatura(3); // temperatura tur pentru incalzire

var incalzire = flow.get('incalzire') || false; // true daca avem necesar de incalzire
var iarna = flow.get('iarna') || false; // true daca incalzirea este activa

/////////////////////////////////////////////////////////////////////

if (DHW != 1 && W != acm_low)
    return { topic: 'ot/cmnd/ot_twater', payload: acm_low };

if (DHW == 1 && W != acm_high)
    return { topic: 'ot/cmnd/ot_twater', payload: acm_high };

/////////////////////////////////////////////////////////////////////

if (incalzire && B != ch_high)
    return { topic: 'ot/cmnd/ot_tboiler', payload: ch_high };

if (!incalzire && B != ch_low)
    return { topic: 'ot/cmnd/ot_tboiler', payload: ch_low };

/////////////////////////////////////////////////////////////////////

if (iarna && CH != 1)
        return { topic: 'ot/cmnd/ot_ch', payload: 1 };

if (!iarna && CH == 1)
        return { topic: 'cor/opentherm/cmnd/ot_ch', payload: 0 };

/////////////////////////////////////////////////////////////////////

return null;


function temperatura(tip) {
    var afara = flow.get('afara'); // temperatura exterior
    var pos = parseInt(afara);
    var col = parseInt(tip);

    if (pos < -20) pos = -20;
    if (pos > 20) pos = 20;
    pos += 20;
    
    // coloana 1 = t acm low - uzual
    // coloana 2 = t acm high - baie
    // coloana 3 = t tur low - cand nu exista necesar de incalzire
    // coloana 4 = t tur high - cand avem necesar de incalzire
    corelare = [
        [20,42,46,60], // -20ºC
        [20,42,45,59], // -19
        [20,42,45,59], // -18
        [20,42,44,58], // -17
        [20,42,44,58], // -16
        [20,42,43,57], // -15
        [20,42,43,57], // -14
        [20,42,42,56], // -13
        [20,42,42,56], // -12
        [20,42,41,55], // -11
        [20,42,41,55], // -10
        [20,42,40,54], // -9
        [20,42,40,54], // -8
        [20,42,39,53], // -7
        [20,42,39,53], // -6
        [20,42,38,52], // -5
        [20,42,38,52], // -4
        [20,42,37,51], // -3
        [20,42,37,51], // -2
        [20,42,36,51], // -1
        [20,42,36,40], // 0ºC
        [20,42,35,39], // 1
        [20,42,35,39], // 2
        [20,42,34,38], // 3
        [20,42,34,38], // 4
        [20,42,33,37], // 5
        [20,42,33,37], // 6
        [20,42,32,36], // 7
        [20,42,32,36], // 8
        [20,42,31,35], // 9
        [20,42,30,34], // 10
        [20,42,29,33], // 11
        [20,42,28,32], // 12
        [20,42,27,31], // 13
        [20,42,26,30], // 14
        [20,42,25,29], // 15
        [20,42,24,28], // 16
        [20,42,23,28], // 17
        [20,42,22,28], // 18
        [20,42,21,28], // 19
        [20,42,20,28]  // 20ºC
    ];

    return corelare[pos][col];
}


Nu rămâne decât să transmitem rezultatul prin MQTT cu un not MQTT out fără reținere:




Pe lângă cele de mai sus avem nevoie de temperatura exterioară, variabila 'iarna' și variabila 'incalzire'.


Să le luăm pe rând, mai întâi temperatura exterioară:


pe care o preluam de la un senzor de temperatura bluetooth - termohigrometrul LYWSD03MMC - cu un nod MQTT in configurat astfel


în nodul funcție nu facem altceva decât să salvăm în flow valoarea temperaturii exterioare

flow.set('afara', msg.payload.Temperature);

/*
senzorul are următorul tip de mesaj:
{
  "Time": "2024-01-13T14:58:46",
  "mac": "a4c138000000",
  "Temperature": 3.2,
  "Humidity": 64,
  "DewPoint": -3.2,
  "Battery": 67,
  "RSSI": -90,
  "TempUnit": "C"
}
*/





Variabila iarna


o preluăm cu un nod MQTT in


și o salvăm în flow cu un nod funcție

flow.set('iarna', msg.payload);





Variabila 'incalzire' o automatizăm cu un termostat.
Pentru această automatizare avem nevoie de

node-red-contrib-ramp-thermostat
node-red-contrib-timeout

iar rezultatul final o să arate așa:


cu nodul MQTT in "t living" preluăm temperatura de la un senzor de temperatura bluetooth - termohigrometrul LYWSD03MMC


Nodul timeout numit siguranță îl configurăm astfel


Datele senzorului de temperatură le prelucrăm într-un nod funcție așa

var iarna = flow.get('iarna');

msg.topic = 'setCurrent';
msg.payload = msg.payload.Temperature;

if (!iarna)
    msg.payload = 90;

return msg;


A doua intrare a nodului termostat este temperatura țintă pe care o preluăm cu un nod MQTT in


iar apoi cu un nod funcție îi modificăm topicul și transmitem mai departe mesajul

msg.topic = 'setTarget';
return msg;




Nodul ramp-termostat are nevoie de mai multă atenție. Deschidem nodul...


adăugăm un profil nou pe care îl numim living, cu 21ºC toată ziua (00:00 - 23:59)


apoi salvăm și nodul termostat va arăta așa:



Pe prima ieșire a nodului termostat avem necesarul de căldură pe care îl salvăm în flow cu un nod funcție:

if (msg.payload)
    flow.set('incalzire',true);
else
    flow.set('incalzire',false);


iar pe ieșirea 3 avem temperatura țintă curentă pe care o salvam cu un nod MQTT out în topicul 'setari/living'


În momentul acesta avem toate elementele gata și putem salva și activa automatizarea.