Slider in Tasmota folosind BerryScript
Inroducere
Am cumpărat de curând un șemineu care poate distribui căldură și la radiatoare (pe scurt: termoșemineu). Pentru a face asta, folosește o pompă de recirculare.
Pompa ar trebui să pornească când apa atinge o anumită temperatură și să se oprească atunci când scade sub o anumită valoare. Deloc complicat până aici, sistemul folosind un termostat simplu care a funcționat acceptabil:
Erau totuși câteva neajunsuri, cum ar fi faptul că nu făcea contact destul de bine cu țeava de căldură, așa că trebuia să pornească puțin mai repede decat ar fi trebuit, ca să compenseze. Pentru că era configurat dintr-o singură setare comună, asta însemna că se și oprea un pic cam târziu, deci nu puteam configura setările cu ușurință.
O altă dorință de-a mea era ca dispozitivul să poată funcționa independent. Dacă se deconectează de la rețea dintr-un motiv oarecare, sau Home Assistant nu mai funcționează, ar trebui să pornească singur ca să nu ajungă să fiarbă apa din instalație. Să zicem că nu vreau să explodeze doar de dragul de a arată cool. Și încă un lucru: ar fi drăguț să se oprească singur, ca să nu recircule apă degeaba.
Sonoff TH Elite cu senzor de temperatură pare să acopere perfect toate cerințele, chiar are în plus și un ecran simpatic pe care se poate vedea temperatura apei. Trebuie să clarific că, din păcate pentru mine, nu sunt sponsorizat pentru acest proiect, iar dispozitivul a fost cumpărat din banii mei.
Totul era perfect, cu excepția unui singur detaliu: nu am găsit un mod de a adauga cu ușurință un slider pentru a controla temperatura. Aș fi putut hardcoda valorile, pentru că în principiu nu ar fi trebui să se schimbe în timp, dar atunci nu aș mai avea cu ce sa mă laud.
Și așa a început aventura...
Să trecem la treaba
Dispozitivul
Nu o să vorbesc despre pregătirea dispozitivului, sunt destule tutoriale despre flashing Tasmota. Codul ar trebui să funcționeze cu orice dispozitiv care rulează Tasmota, de fapt întreg codul din acest tutorial a fost scris și testat pe un controller generic Esp32 găsit într-un sertar.
Berry script
Dacă ai ajuns până aici, probabil că nu ți-ai ars nici controllerul Esp, nici casa. Felicitări, ai trecut cu succes de partea cea mai dificilă!
Toată magia vine de la Berry Script, un limbaj de scripting care permite extinderea funcționalităților Tasmota.
Din păcate, documentația nu este foarte clară când vine vorba de componente noi și există un singur exemplu. Există și un exemplu complet, dar este foarte complex și greu de urmărit.
Dar să începem. În primul rând, trebuie să facem un fișier nou care va conține codul. Pentru a face asta, conectează-te la UI-ul dispozitivului folosind IP-ul lui și mergi la Tools
> Manage File system
și apasă pe Create and Edit new file
.
Introdu numele fișierului sus autoexec.be
, șterge conținutul fișierului și apasă pe Save
. Din acest punct vom edita doar acest fișier. Când mă voi referi la editat, mă voi referi la acest fișier.
Că să ne asigurăm că fișierul se compilează, din meniul principal (Main Menu
), mergi la Tools
> Berry Scripting console
.
Introdu load('autoexec.be')
și apasă pe Run code (or press ‘Enter’ twice)
. Ar trebui să fie afișat output-ul true
.
Setarea componentei
Editează autoexec.be
și introdu codul de mai jos care reprezintă o componentă de baza:
1import webserver
2
3class MySlider
4 def web_add_main_button()
5 webserver.content_send("<div style='padding:0'><h3>Set pump temperature</h3></div>")
6 end
7end
8
9
10d1 = MySlider()
11tasmota.add_driver(d1)
Reîncarcă UI-ul folosind comandăload('autoexec.be')
, așa cum a fost descris mai sus.
În acest moment ar trebui să apară pe ecran textul Set pump temperature
.
Se pare că primul secret a ieșit la iveală, trebuie doar să afișăm niște HTML pentru a genera o nouă componentă.
Ne mai lipsește un lucru, trebuie să salvăm valoarea care se setează folosind slider-ul. Din fericire, lucrurile sunt destul de simple dacă folosim librăria persist
. Putem seta și o valoare implicită atunci când componentă se încarcă prima dată:
1 def init()
2 if (!persist.m_start_temp)
3 persist.m_start_temp = 55
4 end
5
6 if (!persist.m_stop_temp)
7 persist.m_stop_temp = 50
8 end
9 end
Acum avem valori implicite de start și stop pentru temperatura.
Pentru că am nevoie de două valori, una de pornire și una de oprire, voi face o funcție care poate afișa fie una din valori, fie pe cealaltă, iar valoarea se va trimite folosind webserver.content_send
, deci acum metodă web_add_main_button()
va deveni:
1 def web_add_main_button()
2 webserver.content_send("<div style='padding:0'><h3>Set pump temperature</h3></div>")
3 webserver.content_send(
4 self._render_button(persist.m_start_temp, "Start", "start")
5 )
6 webserver.content_send(
7 self._render_button(persist.m_stop_temp, "Stop", "stop")
8 )
9 end
Argumentele sunt: valoarea curentă, o etichetă și un id. Prin id se va identifica elementul care s-a schimbat.
Metodă pentru afișare este:
1 def _render_button(persist_item, label, id)
2 return "<div style='padding:0'>"+
3 "<table style='width: 100%'>"+
4 "<tr>"+
5 "<td><label>"..label.." </label></td>"+
6 "<td align=\"right\"><span id='lab_"..id.."'>"..persist_item.."</span>°C</td>"+
7 "</tr>"+
8 "</table>"+
9 "<input type=\"range\" min=\"20\" max=\"70\" step=\"1\" "+
10 "onchange='la(\"&m_"..id.."_temp=\"+this.value)' "+
11 "oninput=\"document.getElementById('lab_"..id.."').innerHTML=this.value\" "+
12 "value='"..persist_item.."'/>"+
13 "</div>"
14 end
oninput
este un artificiu pentru a schimba valoarea afișată când sliderul se mișcă, așa compensăm pentru faptul că elementul slider din html nu are un mod de a afișa valoarea selectată, așa că nu am ști ce a fost selectat până nu oprim selecția, adică ar fi o experiență neplacută de utilizare.
De fapt onchange
se folosește pentru a schimba valoarea, iar această folosește un pic de magie Tasmota.
Acum că afișăm totul, avem nevoie de un mod de a persista valoarea, acest lucru se face folosind metodă web_sensor
:
1 def web_sensor()
2
3 if webserver.has_arg("m_start_temp")
4 var m_start_temp = int(webserver.arg("m_start_temp"))
5 persist.m_start_temp = m_start_temp
6 persist.save()
7 end
8
9 if webserver.has_arg("m_stop_temp")
10 var m_stop_temp = int(webserver.arg("m_stop_temp"))
11 persist.m_stop_temp = m_stop_temp
12 persist.save()
13 end
14
15 end
Deja putem pune totul cap la cap, avem tot ce era necesar. Totuși mai este ceva ce-mi doresc: să pot vedea valoarea selectată în Home Assistant (dacă este folosit). În caz că am uitat ce este setat, ar fi interesant să afișăm valoarea undeva prin Home Assistant. Pentru a exporta valorile trebuie implementat json_append
:
1 def json_append()
2 var start = int(persist.m_start_temp)
3 var stop = int(persist.m_stop_temp)
4 var msg = strîng.format(",\"Pump\":{\"start\":%i,\"stop\":%i}", start, stop)
5 tasmota.response_append(msg)
6 end
Acum chiar avem un exemplu complet! Să-l vedem:
1import webserver
2import persist
3import strîng
4
5class MySlider
6
7 def init()
8 if (!persist.m_start_temp)
9 persist.m_start_temp = 55
10 end
11
12 if (!persist.m_stop_temp)
13 persist.m_stop_temp = 50
14 end
15
16 end
17
18 def web_add_main_button()
19 webserver.content_send("<div style='padding:0'><h3>Set pump temperature</h3></div>")
20 webserver.content_send(
21 self._render_button(persist.m_start_temp, "Start", "start")
22 )
23 webserver.content_send(
24 self._render_button(persist.m_stop_temp, "Stop", "stop")
25 )
26 end
27
28 def _render_button(persist_item, label, id)
29 return "<div style='padding:0'>"+
30 "<table style='width: 100%'>"+
31 "<tr>"+
32 "<td><label>"..label.." </label></td>"+
33 "<td align=\"right\"><span id='lab_"..id.."'>"..persist_item.."</span>°C</td>"+
34 "</tr>"+
35 "</table>"+
36 "<input type=\"range\" min=\"20\" max=\"70\" step=\"1\" "+
37 "onchange='la(\"&m_"..id.."_temp=\"+this.value)' "+
38 "oninput=\"document.getElementById('lab_"..id.."').innerHTML=this.value\" "+
39 "value='"..persist_item.."'/>"+
40 "</div>"
41 end
42
43
44 def web_sensor()
45
46 if webserver.has_arg("m_start_temp")
47 var m_start_temp = int(webserver.arg("m_start_temp"))
48 persist.m_start_temp = m_start_temp
49 persist.save()
50 end
51
52 if webserver.has_arg("m_stop_temp")
53 var m_stop_temp = int(webserver.arg("m_stop_temp"))
54 persist.m_stop_temp = m_stop_temp
55 persist.save()
56 end
57
58 end
59
60 def json_append()
61 var start = int(persist.m_start_temp)
62 var stop = int(persist.m_stop_temp)
63 var msg = strîng.format(",\"Pump\":{\"start\":%i,\"stop\":%i}", start, stop)
64 tasmota.response_append(msg)
65 end
66end
67
68slider = MySlider()
69tasmota.add_driver(slider)
Este destul de mult cod, dar sper că nu este foarte greu de înțeles.
Setarea automatizării
Acum că avem valorile, putem seta automatizarea efectivă. Regulile sunt foarte simple:
- când se atinge temperatura de start => pornește pompa;
- când temperatura coboară sub valoarea de oprire => oprește pompa.
1def heater_control(value)
2
3 if value >= persist.m_start_temp
4 tasmota.set_power(0, true)
5 end
6
7 if value < persist.m_stop_temp
8 tasmota.set_power(0, false)
9 end
10end
11# această este specifică senzorului Sonoff TH Elite
12tasmota.add_rule("DS18B20#Temperature", heater_control)
Dacă s-a folosit alt dispozitiv, este posibil să aibă alt nume față de "DS18B20#Temperature"
, și probabil va trebui schimbat cu cel potrivit.
Noua interfață Sonoff TH Elite ar trebui să arate cam așa:
Concluzie
Folosind cod destul de simplu, poți transforma un dispozitiv inteligent (dar nu foarte interesant) în ceva cu care să te poți lăuda la prieteni. Valorile se pot seta acum cu ușurință într-o manieră vizuală, iar rezultatul se poate vedea în Home Assistant.