├── images
├── kvs.jpg
├── Vork1.jpg
├── Vork2-4.jpg
├── Vork5-1.jpg
├── Vork5-2.jpg
├── CopyCode.jpg
├── KvsSystem.jpg
├── ShellyKVS.jpg
├── Heating24_10.jpg
├── Heating4_1.jpg
├── ThermiaVilla.jpg
├── editschedule.jpg
├── insertcode.jpg
├── oneschedule.jpg
├── parameters.jpg
├── HeatingCurve12.jpg
├── HeatingCurve24.jpg
├── HeatingWindow.jpg
├── ScheduleLimit.jpg
├── ThermiaShelly.jpg
├── CouldntGetScript.jpg
├── InvalidSchedule.jpg
├── ThermiaEkoClassic.jpg
├── kvsConfigSettings.jpg
├── marketprice_example.jpg
├── marketpriceexample.jpg
├── kvsConfigSettingsJson.jpg
└── ShellyVirtualComponents.jpg
├── files
├── HeatingCurveGraph.xlsx
├── watchdog-min.js
└── watchdog.js
├── manifest.json
├── LICENSE
├── Readme-ET.md
├── README.md
└── SmartHeatingWidthShelly.js
/images/kvs.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/kvs.jpg
--------------------------------------------------------------------------------
/images/Vork1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/Vork1.jpg
--------------------------------------------------------------------------------
/images/Vork2-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/Vork2-4.jpg
--------------------------------------------------------------------------------
/images/Vork5-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/Vork5-1.jpg
--------------------------------------------------------------------------------
/images/Vork5-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/Vork5-2.jpg
--------------------------------------------------------------------------------
/images/CopyCode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/CopyCode.jpg
--------------------------------------------------------------------------------
/images/KvsSystem.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/KvsSystem.jpg
--------------------------------------------------------------------------------
/images/ShellyKVS.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/ShellyKVS.jpg
--------------------------------------------------------------------------------
/images/Heating24_10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/Heating24_10.jpg
--------------------------------------------------------------------------------
/images/Heating4_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/Heating4_1.jpg
--------------------------------------------------------------------------------
/images/ThermiaVilla.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/ThermiaVilla.jpg
--------------------------------------------------------------------------------
/images/editschedule.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/editschedule.jpg
--------------------------------------------------------------------------------
/images/insertcode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/insertcode.jpg
--------------------------------------------------------------------------------
/images/oneschedule.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/oneschedule.jpg
--------------------------------------------------------------------------------
/images/parameters.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/parameters.jpg
--------------------------------------------------------------------------------
/images/HeatingCurve12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/HeatingCurve12.jpg
--------------------------------------------------------------------------------
/images/HeatingCurve24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/HeatingCurve24.jpg
--------------------------------------------------------------------------------
/images/HeatingWindow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/HeatingWindow.jpg
--------------------------------------------------------------------------------
/images/ScheduleLimit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/ScheduleLimit.jpg
--------------------------------------------------------------------------------
/images/ThermiaShelly.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/ThermiaShelly.jpg
--------------------------------------------------------------------------------
/files/HeatingCurveGraph.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/files/HeatingCurveGraph.xlsx
--------------------------------------------------------------------------------
/images/CouldntGetScript.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/CouldntGetScript.jpg
--------------------------------------------------------------------------------
/images/InvalidSchedule.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/InvalidSchedule.jpg
--------------------------------------------------------------------------------
/images/ThermiaEkoClassic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/ThermiaEkoClassic.jpg
--------------------------------------------------------------------------------
/images/kvsConfigSettings.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/kvsConfigSettings.jpg
--------------------------------------------------------------------------------
/images/marketprice_example.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/marketprice_example.jpg
--------------------------------------------------------------------------------
/images/marketpriceexample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/marketpriceexample.jpg
--------------------------------------------------------------------------------
/images/kvsConfigSettingsJson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/kvsConfigSettingsJson.jpg
--------------------------------------------------------------------------------
/images/ShellyVirtualComponents.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LeivoSepp/Smart-heating-management-with-Shelly/HEAD/images/ShellyVirtualComponents.jpg
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "fname": "SmartHeatingWidthShelly.js",
4 | "title": "Budget-friendly smart heating with energy market price.",
5 | "description": "This Shelly script is designed to retrieve energy market prices from Elering and activate heating during the most cost-effective hours each day, employing various algorithms."
6 | }
7 | ]
--------------------------------------------------------------------------------
/files/watchdog-min.js:
--------------------------------------------------------------------------------
1 | let scId=0;function strt(){Shelly.call("KVS.Get",{key:"SmartHeatingSys"+scId},(function(e){e&&delS(JSON.parse(e.value))}))}function delS(e){Shelly.call("Schedule.Delete",{id:e.ExistingSchedule},(function(e,t,d,l){0!==t?print("Script #"+scId,"schedule ",l.id," deletion by watchdog failed."):print("Script #"+scId,"schedule ",l.id," deleted by watchdog.")}),{id:e.ExistingSchedule}),updK(e)}function updK(e){e.ExistingSchedule=0,Shelly.call("KVS.set",{key:"SmartHeatingSys"+scId,value:JSON.stringify(e)})}Shelly.addStatusHandler((function(e){"script"!==e.name||e.delta.running||(scId=e.id,strt())}));
--------------------------------------------------------------------------------
/files/watchdog.js:
--------------------------------------------------------------------------------
1 | //This is a watchdog reference code
2 | let scId = 0;
3 | Shelly.addStatusHandler(function (res) {
4 | if (res.name === 'script' && !res.delta.running) {
5 | scId = res.id;
6 | strt();
7 | }
8 | });
9 | function strt() {
10 | Shelly.call('KVS.Get', { key: "SmartHeatingSys" + scId },
11 | function (res) {
12 | if (res) {
13 | delS(JSON.parse(res.value));
14 | }
15 | });
16 | }
17 | function delS(sDat) {
18 | Shelly.call("Schedule.Delete", { id: sDat.ExistingSchedule },
19 | function (res, err, msg, data) {
20 | if (err !== 0) { print('Script #' + scId, 'schedule ', data.id, ' deletion by watchdog failed.'); }
21 | else { print('Script #' + scId, 'schedule ', data.id, ' deleted by watchdog.'); }
22 | }, { id: sDat.ExistingSchedule }
23 | );
24 | updK(sDat);
25 | }
26 | function updK(sDat) {
27 | sDat.ExistingSchedule = 0;
28 | Shelly.call("KVS.set", { key: "SmartHeatingSys" + scId, value: JSON.stringify(sDat) },);
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License (MIT)
2 |
3 | Copyright (c) 2024-2025 Leivo Sepp
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Readme-ET.md:
--------------------------------------------------------------------------------
1 | # Nutikas ja odav börsihinna järgi kütmine Shellyga
2 |
3 | > [!TIP]
4 | > See Shelly skript tagab kütteseadme töö kõige odavamate tundide ajal, kasutades elektri börsihindu ja erinevaid algoritme.
5 |
6 | > [!IMPORTANT]
7 | > Alates 1. oktoobrist 2025 kasutab Elering 15-min elektrihinda, mis tähendab, et nende API struktuur on muutunud. Et sinu Shelly automatiseerimine töötaks edasi, pead:
8 | >
9 | > ✅ Uuendama oma Shelly skripti
10 | >
11 | > * Minimaalne nõutud versioon: 4.8 või uuem
12 | > * Põhjus: Vanemad skriptid eeldavad tunnipõhiseid hindu, kuid nüüd tagastab API 15-minutilisi intervalle.
13 |
14 | > [!IMPORTANT]
15 | > Alates 1. oktoobrist 2025 kasutab Elering 15-min elektrihinda, mis tähendab neli korda rohkem andmeid. See suurendab oluliselt mälukasutust (kuni 22kB), mistõttu saavad Shelly seadmed nüüd käivitada ainult ühe skripti seadme kohta. Palun veendu, et nüüdsest on iga skripti jaoks eraldi Shelly seade.
16 | > Seda ei ole veel Gen-4 seadmetega testitud — need võivad toetada suuremat skriptimälu.
17 |
18 |
19 | - [Nutikas ja odav börsihinna järgi kütmine Shellyga](#nutikas-ja-odav-börsihinna-järgi-kütmine-shellyga)
20 | - [Põhifunktsioonid](#põhifunktsioonid)
21 | - [Jälgimine ja ajakava muutmine](#jälgimine-ja-ajakava-muutmine)
22 | - [Kuidas kontrollida ajakava](#kuidas-kontrollida-ajakava)
23 | - [Skripti parameetrite konfigureerimine](#skripti-parameetrite-konfigureerimine)
24 | - [Skripti Virtual Componentide häälestamine](#skripti-virtual-componentide-häälestamine)
25 | - [Kuidas panna skript tööle KVS-modes](#kuidas-panna-skript-tööle-kvs-modes)
26 | - [Skripti KVS häälestamine](#skripti-kvs-häälestamine)
27 | - [Heating parameters](#heating-parameters)
28 | - [``"EnergyProvider": "VORK1"``](#energyprovider-vork1)
29 | - [``"AlwaysOnPrice": 10``](#alwaysonprice-10)
30 | - [``"AlwaysOffPrice": 300``](#alwaysoffprice-300)
31 | - [``"InvertedRelay": false``](#invertedrelay-false)
32 | - [``"RelayId": 0``](#relayid-0)
33 | - [``"Country": "ee"``](#country-ee)
34 | - [``"HeatingCurve": 0``](#heatingcurve-0)
35 | - [Kuidas seda skripti installida](#kuidas-seda-skripti-installida)
36 | - [Paigaldamine](#paigaldamine)
37 | - [Kuidas panna tööle kaks installatsiooni](#kuidas-panna-tööle-kaks-installatsiooni)
38 | - [Kuidas panna skript tööle KVS-modes](#kuidas-panna-skript-tööle-kvs-modes-1)
39 | - [Skripti uuendamine](#skripti-uuendamine)
40 | - [Kuidas kontrollida et skript töötab](#kuidas-kontrollida-et-skript-töötab)
41 | - [Kuidas skript töötab](#kuidas-skript-töötab)
42 | - [Oluline teada](#oluline-teada)
43 | - [Testitud rikkestsenaariumid](#testitud-rikkestsenaariumid)
44 | - [Maasoojuspumpade Thermia Villa \& Eko Classic nutikas kütmine Shelly abil](#maasoojuspumpade-thermia-villa--eko-classic-nutikas-kütmine-shelly-abil)
45 | - [Nutikad kütte algoritmid](#nutikad-kütte-algoritmid)
46 | - [Ilmaprognoosi algoritm](#ilmaprognoosi-algoritm)
47 | - [Ilmaprognoosipõhise kütmise eelised](#ilmaprognoosipõhise-kütmise-eelised)
48 | - [Shelly geograafiline asukoht](#shelly-geograafiline-asukoht)
49 | - [Küttegraafik](#küttegraafik)
50 | - [Ajaperioodi algoritm](#ajaperioodi-algoritm)
51 | - [Kas see tõesti vähendab minu elektriarveid](#kas-see-tõesti-vähendab-minu-elektriarveid)
52 | - [Tõrkeotsing](#tõrkeotsing)
53 | - [Viga "Couldn't get script"](#viga-couldnt-get-script)
54 | - [Advanced → Key Value Storage → Script Data](#advanced--key-value-storage--script-data)
55 | - [Litsents](#litsents)
56 | - [Autor](#autor)
57 |
58 |
59 | ## Põhifunktsioonid
60 | 1. **Ilmaennustusega odavad tunnid**: Järgmise päeva küttetunnid on optimeeritud ilmaprognoosi ja energiahindade põhjal.
61 | 2. **Fikseeritud odavad tunnid**: Jaota päev ajaperioodideks (6, 12 või 24) ja aktiveeri küte iga perioodi kõige odavama(te)l tundidel.
62 | 3. **Hinnatasemete kasutamine**: Kasuta min ja max hinnaläve, et hoida Shelly süsteem sellest lähtuvalt ka sees või väljas.
63 |
64 | **Käivituse ajakava**: Skript töötab iga päev pärast 23:00 või vajadusel päeva jooksul, et arvutada järgmise perioodi või päeva küttetunnid.
65 |
66 | ## Jälgimine ja ajakava muutmine
67 |
68 | > [!NOTE]
69 | > Alates skriptiversioonist 3.9 (jaanuar 2025) loob see skript ühe ajakava, mis sisaldab kõiki vajalikke küttetunde.
70 |
71 | ## Kuidas kontrollida ajakava
72 | Skripti loodud küttetundide vaatamiseks:
73 | 1. Avage Shelly ajakava (Schedule).
74 | 2. Klõpsake **Time**, et näha täielikku kütteseadme ajakava.
75 |
76 | |||
77 | |-|-|
78 | |
|
|
79 |
80 | > [!TIP]
81 | > Saate ajakava käsitsi muuta, klõpsates mis tahes tunnil, et see lisada või eemaldada, seejärel klõpsake Next → Next → Save.
82 | > Järgmine kord kui skript arvutab uue ajakava, kirjutatakse kõik käsitsi loodudmuudatused üle.
83 |
84 | **Kuidas jälgida skripti käivitumist**
85 | Andmeväli ``LastCalculation`` KVS-is värskendatakse iga kord, kui Eleringist on saadud uued elektrihinnad ja genereeritakse järgmise perioodi ajakava.
86 | Andmeväli ``ExistingSchedule`` KVS-s on antud skripti poolt loodud schedule ID.
87 |
88 | ## Skripti parameetrite konfigureerimine
89 |
90 | ### Skripti Virtual Componentide häälestamine
91 | > [!TIP]
92 | > See skript kasutab **Virtuaalseid Komponente**, mis võimaldab kõiki seadeid hallata otse Shelly Cloud veebilehelt või mobiilirakendusest.
93 |
94 | Virtuaalseid komponente toetatakse Shelly Gen 2 Pro seadmetes ja kõigis Gen 3 ja uuemates seadmetes.
95 |
96 |
97 |
98 | ### Kuidas panna skript tööle KVS-modes
99 |
100 | > [!TIP]
101 | > See skript võib käia ka KVS-modes isegi kui Virtuaalsed Komponendid on saadaval.
102 |
103 | Seda häälestust on vaja juhul, kui antud Shelly seadme peal on juba mõni teine skript mis kasutab Virtuaalseid Komponente.
104 | Ava skript ja pane ManualKVS parameeter ``mnKv: true``. Peale seda installeerub skript KVS-modes.
105 |
106 | ```js
107 | let c = {
108 | tPer: 24, // KVS:TimePeriod VC:Heating Period (h) 24/12/6/0
109 | hTim: 10, // KVS:HeatingTime VC:Heating Time (h/period)
110 | isFc: false, // KVS:IsForecastUsed VC:Forecast Heat
111 | pack: "VORK2", // KVS:EnergyProvider VC:Network Package (NONE, VORK1, VORK2, VORK4, VORK5, PARTN24, PARTN24PL, PARTN12, PARTN12PL)
112 | lowR: 1, // KVS:AlwaysOnPrice VC:Heat On (min price) (EUR/MWh)
113 | higR: 300, // KVS:AlwaysOffPrice VC:Heat Off (max price) (EUR/MWh)
114 | Inv: false, // KVS:InvertedRelay VC:Inverted Relay
115 | rId: 0, // KVS:RelayId VC: N/A, always first relay (0)
116 | cnty: "ee", // KVS:Country VC:Market Price Country (ee, fi, lv, lt)
117 | hCur: 0, // KVS:HeatingCurve VC:Heating Curve
118 | tmr: 60, // Default timer
119 | pFac: 0.5, // Power factor
120 | mnKv: false, // Forcing script to KVS mode (true) or Virtual components mode (false)
121 | }
122 | ```
123 |
124 | ### Skripti KVS häälestamine
125 |
126 | Kui skript on **KVS-modes**, saab seadeid muuta seadme veebilehe kaudu, kasutades selle IP-aadressi: Menu → Advanced → KVS.
127 | Kõik kasutaja häälestused asuvad JSON formaadis parameetri ``SmartHeatingConf`` all.
128 |
129 | > [!IMPORTANT]
130 | > Alates versioonist 4.2, hoitakse KVS-s andmeid JSON formaadis.
131 | > See aitab kaasa paremale mäluhaldusele, muuda Shelly töö stabiilsemaks ning võimaldab mitu samaaegset installatsiooni.
132 |
133 |
134 |
135 |
136 |
137 | #### Heating parameters
138 |
139 | ```
140 | "TimePeriod": 24,
141 | "HeatingTime": 10,
142 | "IsForecastUsed": true,
143 | ```
144 |
145 | Vaata küttereziimide osas alltoodud tabelit.
146 |
147 | > Küttereziime saate kohandada viisil, et need sobiksid teie isiklike eelistuste ja konkreetsete olukordadega.
148 |
149 | | Kütte režiim | Kirjeldus | Parim kasutus |
150 | | --- | --- | --- |
151 | | ``TimePeriod": 24,``
``"HeatingTime": 10,``
``"IsForecastUsed": true`` | Kütmise aeg **24-tunnise** perioodi kohta sõltub **välistemperatuurist**. | Betoonpõranda kütmine või suur veepaak, mis suudab hoida soojusenergiat vähemalt 10–15 tundi. |
152 | | ``TimePeriod": 12,``
``"HeatingTime": 5,``
``"IsForecastUsed": true`` | Kütmise aeg iga **12-tunnise** perioodi kohta sõltub **välistemperatuurist**. | Kipsivalu põrandaküte või veepaak, mis suudab hoida soojusenergiat 5–10 tundi. |
153 | | ``TimePeriod": 6,``
``"HeatingTime": 2,``
``"IsForecastUsed": true`` | Kütmise aeg iga **6-tunnise** perioodi kohta sõltub **välistemperatuurist**. | Õhk-soojuspumbad, radiaatorid või põrandaküttesüsteemid väikese veepaagiga, mis suudab hoida energiat 3–6 tundi. |
154 | | ``TimePeriod": 24,``
``"HeatingTime": 20,``
``"IsForecastUsed": false`` | Küte on aktiveeritud **20** kõige odavamal tunnil päevas. | Näiteks ventilatsioonisüsteem. |
155 | | ``TimePeriod": 24,``
``"HeatingTime": 12,``
``"IsForecastUsed": false`` | Küte on aktiveeritud **12** kõige odavamal tunnil päevas. | Suur veepaak 1000L või rohkem. |
156 | | ``TimePeriod": 12,``
``"HeatingTime": 6,``
``"IsForecastUsed": false`` | Küte on aktiveeritud **kuuel** kõige odavamal tunnil igas **12-tunnises** perioodis. | Suur veepaak 1000L või rohkem, suure kasutusega. |
157 | | ``TimePeriod": 12,``
``"HeatingTime": 2,``
``"IsForecastUsed": false`` | Küte on aktiveeritud **kahel** kõige odavamal tunnil igas **12-tunnises** perioodis. | Väike 150L veeboiler väikesele majapidamisele. |
158 | | ``TimePeriod": 6,``
``"HeatingTime": 2,``
``"IsForecastUsed": false`` | Küte on aktiveeritud **kahel** kõige kulutõhusamal tunnil igas **6-tunnises** perioodis. | Suur 200L veeboiler neljale või enamale inimesele mõeldud majapidamisele. |
159 | | ``TimePeriod": 0,``
``"HeatingTime": 0,``
``"IsForecastUsed": false`` | Küte on aktiveeritud ainult tundidel, kui elektri börsihind on madalam kui määratud ``alwaysOnLowPrice``. |
160 |
161 | #### ``"EnergyProvider": "VORK1"``
162 | Elektrilevi või Imatra elektri ülekandetasude pakett. Valikus on VORK1, VORK2, VORK4, VORK5, Partner24, Partner24Plus, Partner12, Partner12Plus ja NONE. Vali None, et mitte arvestada ülekandetasusid.
163 | Ülekandetasude üksikasjad leiab siit: [Elektrilevi](https://elektrilevi.ee/en/vorguleping/vorgupaketid/eramu) või [Imatra](https://imatraelekter.ee/vorguteenus/vorguteenuse-hinnakirjad/).
164 |
165 | | Võrgupakett | Kirjeldus | |
166 | | - | - | :-: |
167 | | ``VORK1`` | **Elektrilevi**
Päev/öö 77 EUR/MWh |
|
168 | | ``VORK2`` | **Elektrilevi**
Päeval 60 EUR/MWh
Öösel 35 EUR/MWh |
|
169 | | ``VORK4`` | **Elektrilevi**
Päeval 37 EUR/MWh
Öösel 21 EUR/MWh |
|
170 | | ``VORK5`` | **Elektrilevi**
Päeval 53 EUR/MWh
Öösel 30 EUR/MWh
Päeva tipp 82 EUR/MWh
Puhke tipp 47 EUR/MWh |
|
171 | | ``PARTN24`` | **Imatra**
Päev/öö 60 EUR/MWh | |
172 | | ``PARTN24P`` | **Imatra**
Päev/öö 39 EUR/MWh | |
173 | | ``PARTN12`` | **Imatra**
Päeval 72 EUR/MWh
Öösel 42 EUR/MWh | Suveaeg päev: E-R kell 8:00–24:00.
Öö: E-R kell 0:00–08:00, L-P terve päev
Talveaeg päev: E-R kell 7:00–23:00.
Öö: E-R kell 23:00–7:00, L-P terve päev |
174 | | ``PARTN12P`` | **Imatra**
Päeval 46 EUR/MWh
Öösel 27 EUR/MWh | Suveaeg päev: E-R kell 8:00–24:00.
Öö: E-R kell 0:00–08:00, L-P terve päev
Talveaeg päev: E-R kell 7:00–23:00.
Öö: E-R kell 23:00–7:00, L-P terve päev |
175 | | ``NONE`` | Võrgutasu on 0 ||
176 |
177 | #### ``"AlwaysOnPrice": 10``
178 | Küte on igal juhul sees, kui börsihind on sellest väärtusest madalam (EUR/MWh).
179 |
180 | #### ``"AlwaysOffPrice": 300``
181 | Küte on igal juhul väljas, kui börsihind ületab selle väärtuse (EUR/MWh).
182 |
183 | #### ``"InvertedRelay": false``
184 | Konfigureerib relee oleku kas normaalseks või pööratud.
185 | * ``true`` - Pööratud relee olek. Seda nõuavad mitmed maasoojuspumbad nagu Nibe või Thermia.
186 | * ``false`` - Normaalne relee olek, seda kasutatakse veeboilerite või elektrilise põrandakütte puhul.
187 |
188 | #### ``"RelayId": 0``
189 | Shelly relay ID on vaikimisi 0, kuid mitme väljundiga Shelly puhul tähistab see relee ID numbrit.
190 |
191 |
192 |
193 | #### ``"Country": "ee"``
194 | Börsihinna riik. Toetatud on ainult Eleringi API riigid. * ``ee`` - Eesti * ``fi`` - Soome * ``lt`` - Leedu * ``lv`` - Läti
195 |
196 | #### ``"HeatingCurve": 0``
197 | Ilmaennustuse prognoosi mõju suurendamine või vähendamine küttetundidele. Vaikeväärtus on ``0``, nihe 1 võrdub 1h. See seadistus kehtib ainult siis, kui kasutatakse ilmaprognoosiga kütteaga.
198 | Vaadake kütte kõvera mõju kütte aja sõltuvusgraafikutele: [kütteaja sõltuvusgraafikud](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly?tab=readme-ov-file#heating-curve).
199 | * ``-6`` - 6h vähem kütmist
200 | * ``6`` - 6h rohkem kütmist
201 |
202 | # Kuidas seda skripti installida
203 |
204 | ## Paigaldamine
205 |
206 | 1. Hankige Shelly Plus, Pro või Gen3 seade: [Shelly seadmed](https://www.shelly.com/collections/smart-switches-dimmers).
207 | 2. Ühendage Shelly seade oma isiklikku WiFi võrku. [Shelly veebiliidese juhendid](https://kb.shelly.cloud/knowledge-base/web-interface-guides).
208 | 5. Avage Shelly seadme veebileht: Klõpsake Settings → Device Information → Device IP → klõpsake IP-aadressil. Avaneb Shelly seadme veebileht, vasakpoolses menüüs klõpsake "<> Scripts".
209 | 6. Klõpsake nuppu "Create Script"
210 | 1. Avage skripti veebileht [Githubis](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly/blob/master/SmartHeatingWidthShelly.js).
211 | 2. Klõpsake nuppu "Copy raw file". Nüüd on skript teie lõikelauamälus.
212 |
213 |
214 |
215 | 1. Kleepige kood lõikelaualt skripti aknasse **Ctrl+V**.
216 | 2. Nimetage skript näiteks "Küte 24h-Ilmaprognoos" ja salvestage.
217 | 3. Kui salvestamisprotsess on lõpule viidud, klõpsake "Start".
218 | 4. Skripti parameetrite konfigureerimine
219 | - [Shelly Virtual Component kasutamine](#shelly-rakenduse-kasutamine)
220 | - [Shelly KVS-i kasutamine](#shelly-kvs-i-kasutamine)
221 |
222 | ## Kuidas panna tööle kaks installatsiooni
223 |
224 | Kui Virtual Componendid on saadaval, siis esimene installatsioon kasutab neid ja ka häälestus käib nende kaudu.
225 | Teine installatsioon saab käia paralleelselt KVS-modes. Selleks on tarvis muuta enne skritpi käivitamist parameetrit ``mnKv: true``.
226 |
227 | ### Kuidas panna skript tööle KVS-modes
228 |
229 | > [!TIP]
230 | > See skript võib käia ka KVS-modes isegi kui Virtuaalsed Komponendid on saadaval.
231 |
232 | Seda häälestust on vaja juhul, kui antud Shelly seadme peal on juba mõni teine skript mis kasutab Virtuaalseid Komponente.
233 | Ava skript ja pane ManualKVS parameeter ``mnKv: true``. Peale seda installeerub skript KVS-modes.
234 |
235 | ```js
236 | let c = {
237 | tPer: 24, // KVS:TimePeriod VC:Heating Period (h) 24/12/6/0
238 | hTim: 10, // KVS:HeatingTime VC:Heating Time (h/period)
239 | isFc: false, // KVS:IsForecastUsed VC:Forecast Heat
240 | pack: "VORK2", // KVS:EnergyProvider VC:Network Package (NONE, VORK1, VORK2, VORK4, VORK5, PARTN24, PARTN24PL, PARTN12, PARTN12PL)
241 | lowR: 1, // KVS:AlwaysOnPrice VC:Heat On (min price) (EUR/MWh)
242 | higR: 300, // KVS:AlwaysOffPrice VC:Heat Off (max price) (EUR/MWh)
243 | Inv: false, // KVS:InvertedRelay VC:Inverted Relay
244 | rId: 0, // KVS:RelayId VC: N/A, always first relay (0)
245 | cnty: "ee", // KVS:Country VC:Market Price Country (ee, fi, lv, lt)
246 | hCur: 0, // KVS:HeatingCurve VC:Heating Curve
247 | tmr: 60, // Default timer
248 | pFac: 0.5, // Power factor
249 | mnKv: false, // Forcing script to KVS mode (true) or Virtual components mode (false)
250 | }
251 | ```
252 |
253 | ## Skripti uuendamine
254 |
255 |
256 | > [!WARNING]
257 | > Versioonilt 4.1 ei ole uuendamine võimalik, kuna KVS andmevorming on uuemates versioonides JSON.
258 | > Peale intallatsiooni, on vaja häälestused käsitsi üle käia nii Virtual Komponentide kui ka KVS-i puhul.
259 |
260 | 1. Avage skripti veebileht [Githubis](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly/blob/master/SmartHeatingWidthShelly.js).
261 | 2. Klõpsake nuppu "Copy raw file". Nüüd on skript teie lõikelauamälus.
262 | 3. Avage Shelly seadme veebilehelt: navigeerige Settings → Device Information → Device IP → klõpsake IP-aadressil. Avaneb Shelly seadme veebileht; vasakpoolses menüüs valige "<> Scripts."
263 | 4. Avage skript, mida soovite uuendada.
264 | 5. Valige kogu skriptikood ja kustutage see **Ctrl+A** → **Kustuta**.
265 | 6. Kleepige kood lõikelaualt skripti aknasse **Ctrl+V**.
266 | 7. Salvestage skript, versioon on nüüd uuendatud.
267 | 8. Kõik konfiguratsioonid jäävad samaks, kuna need on salvestatud KVS-i või virtuaalsetesse komponentidesse.
268 |
269 | ## Kuidas kontrollida et skript töötab
270 |
271 | 1. Shelly rakenduses või veebilehel navigeerige "Schedules" (Ajakavad).
272 | 2. Kontrollige Shelly aktiveerimise ajakava.
273 | 3. Edasijõudnud kasutajad saavad kontrollida KVS-i salvestust: [Advanced → Key Value Storage → Script Data](#advanced--key-value-storage--script-data)
274 |
275 | ## Kuidas skript töötab
276 |
277 | 1. Internetiühendus:
278 | * Skript vajab internetti, et alla laadida igapäevaseid elektrihindu ja ilmaprognoose.
279 | 2. Igapäevane töö:
280 | * Skript töötab iga päev pärast kella 23:00 või vastavalt vajadusele päeva jooksul, et määrata küttetunnid.
281 | 3. Töövoog:
282 | * Skript järgib vooskeemi, et määrata parimad kütmissetunnid turuhindade ja ilmaprognooside põhjal.
283 |
284 |
285 | ```mermaid
286 | flowchart TD
287 | 0@{ shape: circle, label: "Start" } --> A
288 | A[Get Shelly time and location] --> K{Is forecast used?}
289 | K -- Yes --> B{Get forecast
from Open-Meteo.com API}
290 | B -- Succeeded Calculate heating time --> D{Get market price
from Elering API}
291 | K -- No --> D
292 | B --> M@{ shape: subproc, label: "FailedCheck again in 5 minute" }
293 | D -- SucceededCalculate heating schedules --> L{Check, if market price and forecast needs update}
294 | D --> M
295 | L --> M
296 | L -- YesStart the script --> 0
297 | ```
298 |
299 | 4. Watchdog workflow
300 |
301 | ```mermaid
302 | flowchart TD
303 | 0@{ shape: circle, label: "Start" } --> A
304 | A[Create 'watchdog' event handler] --> K{Is heating script stopped or deleted?}
305 | K -- Yes --> B[Find the script scheduleand delete it]
306 | ```
307 |
308 | ## Oluline teada
309 |
310 | *
Kui skript peatatakse, kustutatakse kütte ajakava. Shelly järgib kütte algoritmi ainult siis, kui skript töötab.
311 | *Kaks skripti installatsiooni saab käia korraga, kui esimene neist töötab Virtuaalsete Komponentidega, siis teine saab olla ainult KVS modes. 312 | *
KVS-režiimis võib korraga töötada kuni kaks erineva algoritmiga skripti ühel seadmel. Skriptid võivad kasutada kas sama releeväljundit Shelly Plus 1-ga või erinevaid releeväljundeid, nagu toetab näiteks Shelly Plus 2PM.
313 | *See skript loob spetsiaalse "valvuri/watchdog" skripti. See "valvuri" skript kustutab Shelly kütte ajakava kui põhiskript peatatakse või kustutatakse.
314 | *Interneti katkestuste mõju vähendamiseks kasutab see skript parameetrit ``heating time``, et lülitada küte ajalooliselt odavamate tundide järgi sisse.
315 | *Selle skripti "Run on startup" nupp peab olema aktiveeritud. See seadistus tagab, et skript käivitub pärast voolukatkestust, Shelly taaskäivitust või püsivara uuendamist.
316 | *See skript haldab ainult tema enda loodud kütte ajakava. See skript kustutab ainult selle ajakava, mille ise on loonud.
317 | *See lahendus on kasulik ainult siis, kui teil on börsihinnaga elektrileping. Kui teie elektrileping on kindla hinnaga, siis antud lahendus ei aita rahalist kokkuhoidu saavutada.
318 | * See skript sõltub internetist ja kahest teenusest: 319 | * Elektrituru hind [Eleringi API](https://dashboard.elering.ee/assets/api-doc.html#/nps-controller/getPriceUsingGET), 320 | * Ilmaprognoos [Open-Meteo API](https://open-meteo.com/en/docs). 321 | *Shelly Gen2 Plus seadmete püsivara peab olema versioon 1.4.4 või uuem. KVS andmed on kirjutuskaitstud, kui püsivara versioon on 1.4.3 või vanem. 322 | *
Shelly Gen2 Pro või Gen3 seadmete püsivara peab olema versioon 1.4.4 või uuem. Skript ei installi virtuaalseid komponente, kui püsivara versioon on 1.4.3 või vanem.
323 |
324 | ## Testitud rikkestsenaariumid
325 | Alltoodud rikete ajal kasutab Shelly ``Heating Time`` kestust, et lülitada küte ajalooliselt odavamate tundide järgi sisse.
326 | Internetirikke korral jagab Shelly oma küttetunnid vastavalt häälestatud perioodide vahel.
327 |
328 | **Testitud rikkestsenaariumid**
329 | 1. Shelly töötab edasi, kuid internet läheb maha kodurouteri või internetiteenuse pakkuja rikke tõttu. Shelly kellaaeg jääb korrektseks.
330 | 2. Pärast voolukatkestust internet ei tööta ja Shellyl puudub kellaaeg.
331 | 3. Eleringi HTTP viga tekib ja Eleringi server pole kättesaadav.
332 | 4. Eleringi API rike juhtub ja teenus on maas.
333 | 5. Eleringi API tagastab valed andmed ja hinnad puuduvad.
334 | 6. Ilmaprognoosi HTTP viga tekib ja server pole saadaval.
335 | 7. Ilmaprognoosi API teenuse viga tekib ja JSON andmeid ei saada.
336 |
337 | # Maasoojuspumpade Thermia Villa & Eko Classic nutikas kütmine Shelly abil
338 |
339 | Thermia Villa ja Thermia Eko Classic on küll suhteliselt vanad, kuid siiski hästi tööötavad ja päris populaarsed maasoojuspumbad.
340 | Käesolev juhis nõustab kuidas need soojuspumbad Shelly abil nutikalt kütma panna.
341 |
342 | Instruktsioonid ja ühendused
343 | (Esiteks mõlema soojuspumba installer manualist pilt.)
344 |
345 | |Thermia Villa|Thermia Eko Classic|
346 | |---|---|
347 | |
|
|
348 |
349 | Ühenda **kaks Shelly seadet** maasoojuspumba sees vastavalt **skeemile**.
350 |
351 |
352 |
353 | Alljärgnevast tabelist leiad kuidas **häälestada oma Shelly seadmed**.
354 | Mõlemale Shelly seadmele installeeri Smart Heating skript ja häälesta vastavalt kas kütte või sooja vee tootmise jaoks.
355 |
356 | |Heatpump|Heating+Hot Water|only Hot Water|
357 | |---|---|---|
358 | |Thermia Villa|Shelly 1
``"InvertedRelay": true``|Shelly 2
``"InvertedRelay": true``|
359 | |Thermia Eko Classic|Shelly 1
``"InvertedRelay": true``|Shelly 2
``"InvertedRelay": false``|
360 |
361 | Siin on **soojuspumba tööolukorrad** lihtsalt infoks.
362 |
363 | |Thermia Villa|Thermia Eko Classic|Shelly 1|Shelly 2|
364 | |---|---|---|---|
365 | |EVU Stop
No heating or hot water|Hot water
Reduced Temperatur|On|On|
366 | |Hot Water
Reduced Temperatur|EVU Stop
No heating or Hot water|On|Off|
367 | |Normal heating
Normal Hot water|Normal heating
Normal Hot water|Off|On|
368 | |Normal heating
Normal Hot water|Normal heating
Normal Hot water|Off|Off|
369 |
370 | # Nutikad kütte algoritmid
371 |
372 | ## Ilmaprognoosi algoritm
373 |
374 | > [!TIP]
375 | > See algoritm arvutab järgmise päeva kütteaja ilmaprognooside põhjal.
376 | > See on eriti tõhus erinevate koduküttesüsteemide jaoks, kuna optimeerib küttevajaduse eeldatavate ilmastikuoludega.
377 |
378 | ### Ilmaprognoosipõhise kütmise eelised
379 |
380 | * Välistemperatuurile reageerimine:
381 |
382 | Kui välistemperatuur on +17 kraadi, pole üldjuhul kütmist vaja. Kui aga temperatuur langeb -5 kraadini, on vajalik mõningane kütmine ja eriti külmades tingimustes, nagu -20 kraadi, on vaja märkimisväärselt kütmist. Ilmateade kohandab kütmiseks võetavate tundide arvu vastavalt välistemperatuurile.
383 |
384 | * Nutikas kütte haldamine:
385 |
386 | Ilmaprognooside kasutamine võimaldab nutikat ja kohanduvat küttehaldust. Süsteem kohandab küttetunde välise temperatuuri põhjal, luues reageeriva ja dünaamilise kütte ajakava.
387 |
388 | * Asukohapõhine prognoos:
389 |
390 | Täpse ilmaprognoosi saamiseks on tarvis teada asukohta, et arvutada välja parim küttestrateegia.
391 |
392 | ### Shelly geograafiline asukoht
393 |
394 | > [!IMPORTANT]
395 | > Veenduge, et teie Shelly seadmel oleks õige asukohateave, kontrollides Shelly → Seaded → Geolokatsioon → Laiuskraad/pikkuskraad.
396 |
397 | Märkus: Shelly asukoht määratakse teie internetiteenuse pakkuja IP-aadressi põhjal, mis ei pruugi täpselt kajastada teie kodu asukohta. Kontrollige ja uuendage vajadusel laius- ja pikkuskraadi seadeid.
398 |
399 | ### Küttegraafik
400 |
401 | Temperatuuri ja kütteaja vaheline seos on tuntud kui *küttegraafik*.
402 |
403 | Kütteaega mõjutab teie maja isolatsioon. Näiteks vana ja soojustamata maja võib vajada -5 kraadi juures 10 tundi kütmist, samas kui uus A-klassi maja vajab võib-olla ainult 6 tundi.
404 |
405 | Nende erinevuste arvestamiseks sisaldab skript parameetrit ``heatingCurve``, mis võimaldab kasutajal kohandada küttetegurit vastavalt maja soojapidavusele.
406 |
407 | * 24-tunnise perioodi graafik iseloomustab kuidas kütteaeg varieerub välistemperatuuri ja ``heatingCurve`` parameetri põhjal, mis omakorda nihutab küttegraafikut vasakule või paremale 1h sammuga.
408 |
409 |
410 |
411 | ____
412 |
413 | * 12-tunnise perioodi küttegraafik ja kütteaja sõltuvus välistemperatuuri ja ``heatingCurve`` parameetrist.
414 |
415 |
416 |
417 | Kui matemaatiline pool huvitab siis küttegraafuku lineaarvõrrand on järgmine: ``(Temperatuuri prognoos) * PowerFactor + (Temperatuuri prognoos + heatingCurve)``.
418 |
419 | ## Ajaperioodi algoritm
420 |
421 | > See algoritm jagab päeva perioodideks, aktiveerides kütte kõige odavamatel tundidel igas perioodis. See sobib hästi kasutusjuhtudeks, nagu kuumavee boilerid, kus kasutus sõltub majapidamise suurusest ja mitte välistemperatuurist. See meetod optimeerib energiakasutust ning vesi püsib soe kõige madalamate elektri börsi hindadega.
422 |
423 | * 24-tunnine graafik ja kuidas 10 kõige odavamat tundi valitakse, on näitena kujutatud järgmisel pildil. Punane tähistab kütmiseks kasutatavaid tunde.
424 |
425 |
426 |
427 | ___
428 |
429 | * 4-tunnine graafik ja kuidas 1 kõige odavam kütmise tund valitakse iga 4-tunnise perioodi kestel.
430 |
431 |
432 |
433 |
434 |
435 | # Kas see tõesti vähendab minu elektriarveid
436 | Lühidalt: jah.
437 |
438 | Siin on ka üksikasjalikum selgitus. Kuigi teie üldine igapäevane elektritarbimine jääb samaks, optimeerib see skript teie kütteseadmete töö kõige odavamatele tundidele. Seetõttu väheneb teie elektriarve, kuigi energia tarbimine jääb samaks.
439 |
440 | Sellised seadmed nagu veeboilerid, veepaagid, maakütte- või õhksoojuspumbad, elektriradiaatorid, põrandakütte elektrisüsteemid ja konditsioneerid on seadmed, mis annavad kõige suurema kasu börsihinnaga juhtimisel.
441 |
442 | Elektrihinnad võivad kõikuda märkimisväärselt, varieerudes päeva jooksul kuni 100 korda. Elektrituru hindade kohta lisateabe saamiseks vaadake järgmist linki: [Elering](https://dashboard.elering.ee/et/nps/price)
443 |
444 |
445 | # Tõrkeotsing
446 |
447 | ## Viga "Couldn't get script"
448 |
449 | Shelly süsteemis on probleem, mis võib mõjutada teie kogemust skriptide avamisel Shelly pilve või mobiilirakenduse kaudu. Tekkinud viga "Couldn't get script" on teadaolev bugi, mis takistab skriptide avamist, mis on suuremad kui 15kB nende platvormide kaudu.
450 |
451 | Selle ebamugavuse ületamiseks soovitame järgmisi lahendusi:
452 |
453 | 1. Avage skript seadme veebilehe kaudu:
454 | Juurdepääs seadme veebilehele võimaldab teil edukalt avada mis tahes skripti. See meetod pakub otsest ja usaldusväärset lahendust, et vaadata ja hallata oma skripte sujuvalt.
455 |
456 | 2. Alternatiivne lahendus Shelly pilve kaudu:
457 | Kui seadme veebilehele juurdepääs ei ole võimalik, järgige neid samme Shelly pilves:
458 |
459 | 1. Kustutage olemasolev skript.
460 | 2. Looge uus skript.
461 | 3. Kopeerige ja kleepige kogu skript skripti aknasse.
462 | 4. Salvestage ja sulgege skript.
463 | 5. Käivitage skript.
464 |
465 | Kui selle protsessi käigus tekib probleeme, saate seda lahendust korrata, alustades skripti kustutamise sammust.
466 |
467 |
468 |
469 | ## Advanced → Key Value Storage → Script Data
470 |
471 | Skript salvestab andmed Shelly KVS (Key-Value-Storage) säilitamaks neid elektrikatkestuste või taaskäivituste korral.
472 |
473 | Salvestatud andmete juurde pääsemiseks Shelly seadme veebilehe kaudu, navigeerige **Advanced → KVS**.
474 |
475 | 1. Parameeter: ``ExistingSchedule`` Väärtus: ``1``
476 |
477 | See on skripti poolt loodud ajakava ID number. See teave on oluline iga skripti jaoks, et tuvastada ja hallata seotud ajakava.
478 |
479 | 2. Parameeter: ``LastCalculation`` Väärtus: ``Fri Dec 27 2024 23:29:20 GMT+0200``
480 |
481 | See ajatempel näitab aega, millal skript sai edukalt Eleringi API kaudu börsihinnad ja tekitas kütmise jaoks ajakava. See teave pakub head ülevaadet skripti tegevuse ajakavast.
482 |
483 | 3. Parameeter: ``Version`` Väärtus: ``4.3``
484 |
485 | Versioon näitab installitud skripti versiooni.
486 |
487 |
488 |
489 |
490 | # Litsents
491 |
492 | See projekt on litsentseeritud MIT litsentsi alusel. Vaadake [LITSENTS](LICENSE) faili üksikasjade saamiseks.
493 |
494 | # Autor
495 |
496 | Loodud Leivo Sepp, 2024-2025
497 |
498 | [Smart heating management with Shelly - GitHub Repository](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Smart and cheap heating with Shelly
2 |
3 | > [!TIP]
4 | > This Shelly script is designed to optimize heating activation by leveraging energy market prices from Elering, ensuring heating operates during the most cost-effective hours using various algorithms.
5 |
6 | > [!IMPORTANT]
7 | > Starting October 1, 2025, Elering switched to 15-min electricity price intervals, which means their API structure has changed. To keep your Shelly automation working, you need to:
8 | >
9 | > ✅ Update Your Shelly Script
10 | > * Minimum required version: 4.8 or later
11 | > * Reason: Older scripts assume hourly prices, but now the API returns 15-minute intervals.
12 |
13 | > [!IMPORTANT]
14 | > Starting October 1, 2025, the Elering API now provides electricity market prices in 15-min intervals, resulting in four times more data. This significantly increases memory usage (peak usage 22kB), so Shelly devices can now run only one instance of this script per device. Please ensure that you dedicate a separate Shelly device for each script.
15 | > This has not yet been validated with Gen-4 devices, which may support increased memory for scripting.
16 |
17 |
18 | - [Smart and cheap heating with Shelly](#smart-and-cheap-heating-with-shelly)
19 | - [Key Features](#key-features)
20 | - [Monitoring and edit schedule](#monitoring-and-edit-schedule)
21 | - [How to check the Heating Schedule](#how-to-check-the-heating-schedule)
22 | - [Configuring Script parameters](#configuring-script-parameters)
23 | - [Configuration using Virtual Components](#configuration-using-virtual-components)
24 | - [How to force script to KVS mode](#how-to-force-script-to-kvs-mode)
25 | - [Configuration using KVS](#configuration-using-kvs)
26 | - [Heating parameters](#heating-parameters)
27 | - [``"EnergyProvider": "VORK1"``](#energyprovider-vork1)
28 | - [``"AlwaysOnPrice": 10``](#alwaysonprice-10)
29 | - [``"AlwaysOffPrice": 300``](#alwaysoffprice-300)
30 | - [``"InvertedRelay": false``](#invertedrelay-false)
31 | - [``"RelayId": 0``](#relayid-0)
32 | - [``"Country": "ee"``](#country-ee)
33 | - [``"HeatingCurve": 0``](#heatingcurve-0)
34 | - [How to Install this Script](#how-to-install-this-script)
35 | - [Installation](#installation)
36 | - [How to run two instances of this script](#how-to-run-two-instances-of-this-script)
37 | - [How to force script to KVS mode](#how-to-force-script-to-kvs-mode-1)
38 | - [Updating Script](#updating-script)
39 | - [How to Verify Script Execution](#how-to-verify-script-execution)
40 | - [How the Script Operates](#how-the-script-operates)
41 | - [Important To Know](#important-to-know)
42 | - [Tested Failure Scenarios](#tested-failure-scenarios)
43 | - [Smart Heating for Thermia Villa \& Eko Classic Using Shelly](#smart-heating-for-thermia-villa--eko-classic-using-shelly)
44 | - [Smart Heating Algorithms](#smart-heating-algorithms)
45 | - [Weather Forecast Algorithm](#weather-forecast-algorithm)
46 | - [Advantages of Weather Forecast-Based Heating](#advantages-of-weather-forecast-based-heating)
47 | - [Shelly Geolocation](#shelly-geolocation)
48 | - [Heating Curve](#heating-curve)
49 | - [Time Period Algorithm](#time-period-algorithm)
50 | - [Does it Truly Reduce My Electric Bills](#does-it-truly-reduce-my-electric-bills)
51 | - [Troubleshooting](#troubleshooting)
52 | - [Error "Couldn't get script"](#error-couldnt-get-script)
53 | - [Advanced → Key Value Storage → Script Data](#advanced--key-value-storage--script-data)
54 | - [License](#license)
55 | - [Author](#author)
56 |
57 | ## Key Features
58 | 1. **Dynamic Heating Time Calculation**:
59 | Calculates optimal heating times for the next day based on weather forecasts and energy prices.
60 |
61 | 1. **Time Period Division**:
62 | Divides the day into time periods and activates heating during the cheapest hour within each period.
63 |
64 | 1. **Price-Level Utilization**:
65 | Employs minimum and maximum price thresholds to keep the Shelly system consistently on or off based on cost efficiency.
66 |
67 | 1. **Two script instances can run on same Shelly device**:
68 | Starting from the version 4.2, two script instances are supported on same Shelly device. The script memory usage was reduced from 16kB to 4.3kB and peak memory reduced from 25kB to 12kB.
69 |
70 | **Execution Schedule**:
71 | The script runs daily after 23:00 or as necessary during the day to set up heating time slots for the upcoming period.
72 |
73 | ## Monitoring and edit schedule
74 |
75 | > [!NOTE]
76 | > Starting from the script version 3.9 (January 2025), this script creates a single scheduler using an advanced timespec that includes all the required heating hours.
77 |
78 | ## How to check the Heating Schedule
79 | To view the heating hours created by the script:
80 | 1. Open the schedule.
81 | 2. Click on Time to see the full heating schedule.
82 |
83 | |||
84 | |-|-|
85 | |
|
|
86 |
87 |
88 | > [!TIP]
89 | > You can manually override the schedule by clicking on any hour to include or exclude it for a specific day, then lick Next → Next → Save.
90 | > The next time the script calculates a new schedule, it will generate a fresh schedule with the updated timespec.
91 |
92 | **How to monitor script execution**
93 | The field ``LastCalculation`` in KVS is updated each time electricity prices are retrieved from Elering and a heating schedule is generated for the next heating period.
94 | The field ``ExistingSchedule`` in KVS is the scheduleID created by this script.
95 |
96 | ## Configuring Script parameters
97 |
98 | ### Configuration using Virtual Components
99 | > [!TIP]
100 | > This script supports Shelly Virtual Components, allowing script parameters to be modified remotely using the Shelly app on a mobile phone.
101 |
102 | Virtual Components are supported on Shelly Gen 2 Pro devices, as well as all newer Gen 3 and later devices.
103 |
104 |
105 |
106 | ### How to force script to KVS mode
107 |
108 | > [!TIP]
109 | > This script can be forced to KVS mode even if Virtal Components are available.
110 |
111 | You want to have this in KVS mode in case you have other important scripts already using Virtual Components in the same device.
112 | Open the script and set the ManualKVS parameter ``mnKv: true``. Now the script will install in KVS mode.
113 |
114 | ```js
115 | let c = {
116 | tPer: 24, // KVS:TimePeriod VC:Heating Period (h) 24/12/6/0
117 | hTim: 10, // KVS:HeatingTime VC:Heating Time (h/period)
118 | isFc: false, // KVS:IsForecastUsed VC:Forecast Heat
119 | pack: "VORK2", // KVS:EnergyProvider VC:Network Package (NONE, VORK1, VORK2, VORK4, VORK5, PARTN24, PARTN24PL, PARTN12, PARTN12PL)
120 | lowR: 1, // KVS:AlwaysOnPrice VC:Heat On (min price) (EUR/MWh)
121 | higR: 300, // KVS:AlwaysOffPrice VC:Heat Off (max price) (EUR/MWh)
122 | Inv: false, // KVS:InvertedRelay VC:Inverted Relay
123 | rId: 0, // KVS:RelayId VC: N/A, always first relay (0)
124 | cnty: "ee", // KVS:Country VC:Market Price Country (ee, fi, lv, lt)
125 | hCur: 0, // KVS:HeatingCurve VC:Heating Curve
126 | tmr: 60, // Default timer
127 | pFac: 0.5, // Power factor
128 | mnKv: false, // Forcing script to KVS mode (true) or Virtual components mode (false)
129 | }
130 | ```
131 |
132 | ### Configuration using KVS
133 | If the script in **KVS mode**, then settings can be modified via the device's web page using its IP address: Menu → Advanced → KVS.
134 | All the user settings are stored in JSON format under the key ``SmartHeatingConf``.
135 |
136 | > [!IMPORTANT]
137 | > Starting from the version 4.2, script configuration settings are stored in JSON format in KVS.
138 | > This helps to reduce script memory usage and enables to run two script instances in the same Shelly device.
139 |
140 |
141 |
142 |
143 |
144 | #### Heating parameters
145 |
146 | ```
147 | "TimePeriod": 24,
148 | "HeatingTime": 10,
149 | "IsForecastUsed": true,
150 | ```
151 |
152 | These options are described in the following table.
153 |
154 | > You can customize or change the heating modes to better suit your personal preferences and specific situations. This flexibility allows you to adjust the system based on your needs, energy considerations, and comfort requirements.
155 |
156 | |Heating mode|Description|Proposed usage|
157 | |---|---|---|
158 | |``"TimePeriod": 24,``
``"HeatingTime": 10,``
``"IsForecastUsed": true``|The heating time for **24-hour** period depends on the **outside temperature**.|Concrete floor heating system or big water tank capable of retaining thermal energy for a duration of at least 10 to 15 hours.|
159 | |``"TimePeriod": 12,``
``"HeatingTime": 5,``
``"IsForecastUsed": true``|The heating time for each **12-hour** period depends on the **outside temperature**.|Gypsum (kipsivalu) floor heating system or water tank capable of retaining thermal energy for a duration of 5 to 10 hours.|
160 | |``"TimePeriod": 6,``
``"HeatingTime": 2,``
``"IsForecastUsed": true``|The heating time for each **6-hour** period depends on the **outside temperature**.|Air source heat pumps, radiators or underfloor heating panels with small water tank capable of retaining energy for a duration of 3 to 6 hours.|
161 | |``"TimePeriod": 24,``
``"HeatingTime": 20,``
``"IsForecastUsed": false``|Heating is activated during the **20** most cost-effective hours in a **day**.|Ventilation system.
162 | |``"TimePeriod": 24,``
``"HeatingTime": 12,``
``"IsForecastUsed": false``|Heating is activated during the **12** most cost-effective hours in a **day**.|Big water tank 1000L or more.
163 | |``"TimePeriod": 12,``
``"HeatingTime": 6,``
``"IsForecastUsed": false``|Heating is activated during the **six** most cost-effective hours within every **12-hour** period.|Big water tank 1000L or more with heavy usage.
164 | |``"TimePeriod": 12,``
``"HeatingTime": 2,``
``"IsForecastUsed": false``|Heating is activated during the **two** most cost-effective hours within every **12-hour** period. |A 150L hot water boiler for a little household.
165 | |``"TimePeriod": 6,``
``"HeatingTime": 2,``
``"IsForecastUsed": false``|Heating is activated during the **two** most cost-effective hours within every **6-hour** period.|A 200L hot water boiler for a household with four or more people.
166 | |``"TimePeriod": 0,``
``"HeatingTime": 0,``
``"IsForecastUsed": false``|Heating is only activated during hours when the **price is lower** than the specified ``alwaysOnLowPrice``.|
167 |
168 | #### ``"EnergyProvider": "VORK1"``
169 | Defines the Elektrilevi or Imatra electricity transmission tariff package. Options include VORK1, VORK2, VORK4, VORK5, Partner24, Partner24Plus, Partner12, Partner12Plus, and NONE. Select None to ignore transmission fees.
170 | Please check the details in this [Elektrilevi page](https://elektrilevi.ee/en/vorguleping/vorgupaketid/eramu) or [Imatra page](https://imatraelekter.ee/vorguteenus/vorguteenuse-hinnakirjad/). Options are the following.
171 |
172 | |Network package|Description||
173 | |---|---|-|
174 | |``VORK1``|Elektrilevi
Day and night basic rate 77 EUR/MWh|
|
175 | |``VORK2``|Elektrilevi
Day 60 EUR/MWh
Night 35 EUR/MWh|
|
176 | |``VORK4``|Elektrilevi
Day 37 EUR/MWh
Night 21 EUR/MWh|
|
177 | |``VORK5``|Elektrilevi
Day 53 EUR/MWh
Night 30 EUR/MWh
Day Peak time 82 EUR/MWh
Holiday Peak Time 47 EUR/MWh|
|
178 | |``PARTN24``|Imatra
Day and night basic rate 60 EUR/MWh| |
179 | |``PARTN24PL``|Imatra
Day and night basic rate 39 EUR/MWh| |
180 | |``PARTN12``|Imatra
Day 72 EUR/MWh
Night 42 EUR/MWh| Summer Daytime: MO-FR at 8:00–24:00.
Summer Night time: MO-FR at 0:00–08:00, SA-SU all day
Winter Daytime: MO-FR at 7:00–23:00.
Winter Night time: MO-FR at 23:00–7:00, SA-SU all day |
181 | |``PARTN12PL``|Imatra
Day 46 EUR/MWh
Night 27 EUR/MWh|Summer Daytime: MO-FR at 8:00–24:00.
Summer Night time: MO-FR at 0:00–08:00, SA-SU all day
Winter Daytime: MO-FR at 7:00–23:00.
Winter Night time: MO-FR at 23:00–7:00, SA-SU all day|
182 | |``NONE``|Network fee is set to 0 and it will not taken into account.||
183 |
184 | #### ``"AlwaysOnPrice": 10``
185 | Keep heating always on if the electricity market price lower than this value (EUR/MWh).
186 |
187 | #### ``"AlwaysOffPrice": 300``
188 | Keep heating always OFF if electricity market price higher than this value (EUR/MWh).
189 |
190 | #### ``"InvertedRelay": false``
191 | Configures the relay state to either normal or inverted.
192 | * ``true`` - Inverted relay state. This is required by many heating systems like Nibe or Thermia.
193 | * ``false`` - Normal relay state, used for water heaters.
194 |
195 | #### ``"RelayId": 0``
196 | Configures the Shelly relay ID when using a Shelly device with multiple relays. Default ``0``.
197 |
198 | #### ``"Country": "ee"``
199 | Specifies the country for energy prices. Only countries available in the Elering API are supported.
200 | * ``ee`` - Estonia
201 | * ``fi`` - Finland
202 | * ``lt`` - Lithuania
203 | * ``lv`` - Latvia
204 |
205 | #### ``"HeatingCurve": 0``
206 | Forecast impact increases or decreases the number of hours calculated by the algorithm based on the weather forecast. Default ``0``, shifting by 1 equals 1h. This setting is applicable only if weather forecast used.
207 | Check heating curve impact for [heating time dependency graphs](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly?tab=readme-ov-file#heating-curve).
208 | * ``-6`` - less heating
209 | * ``6`` - more heating
210 |
211 | # How to Install this Script
212 |
213 | ## Installation
214 |
215 | 1. Optain a Shelly Plus, Pro or Gen3 device [Shelly devices](https://www.shelly.com/collections/smart-monitoring-saving-energy).
216 | 2. Connect the Shelly device to your personal WiFi network. Refer to the [Shelly web interface guides.](https://kb.shelly.cloud/knowledge-base/web-interface-guides)
217 |
218 | 5. Open the Shelly device web page: Click Settings → Device Information → Device IP → click on the IP address. The Shelly device web page will open, on the left menu click "<> Scripts".
219 | 6. Click the "Create Script".
220 | 7. Open script from the [Github](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly/blob/master/SmartHeatingWidthShelly.js).
221 | 8. Click the button "Copy raw file". Now the script content is in your clipboard memory.
222 |
223 |
224 | 6. Paste the code from the clipboard to the script window **Ctrl+V**.
225 | 1. Name the script, for instance, "Heating 24h-Forecast", and save.
226 | 2. Click "Start" once the saving process is complete.
227 | 3. Configure Script parameters
228 | - [Using Shelly App Virtual Components](#using-shelly-app)
229 | - [Using Shelly KVS](#using-shelly-kvs)
230 |
231 | ## How to run two instances of this script
232 |
233 | If Virtual Components are supported, then the first instance in installed using Virtual Components. All the configuration is done through the Virtual Components.
234 | The second instance of this script can run only in KVS-mode in the same device.
235 |
236 | ### How to force script to KVS mode
237 |
238 | > [!TIP]
239 | > This script can be forced to KVS mode even if Virtal Components are available.
240 |
241 | You want to have this in KVS mode in case you have other important scripts already using Virtual Components in the same device.
242 | Open the script and set the ManualKVS parameter ``mnKv: true``. Now the script will install in KVS mode.
243 |
244 | ```js
245 | let c = {
246 | tPer: 24, // KVS:TimePeriod VC:Heating Period (h) 24/12/6/0
247 | hTim: 10, // KVS:HeatingTime VC:Heating Time (h/period)
248 | isFc: false, // KVS:IsForecastUsed VC:Forecast Heat
249 | pack: "VORK2", // KVS:EnergyProvider VC:Network Package (NONE, VORK1, VORK2, VORK4, VORK5, PARTN24, PARTN24PL, PARTN12, PARTN12PL)
250 | lowR: 1, // KVS:AlwaysOnPrice VC:Heat On (min price) (EUR/MWh)
251 | higR: 300, // KVS:AlwaysOffPrice VC:Heat Off (max price) (EUR/MWh)
252 | Inv: false, // KVS:InvertedRelay VC:Inverted Relay
253 | rId: 0, // KVS:RelayId VC: N/A, always first relay (0)
254 | cnty: "ee", // KVS:Country VC:Market Price Country (ee, fi, lv, lt)
255 | hCur: 0, // KVS:HeatingCurve VC:Heating Curve
256 | tmr: 60, // Default timer
257 | pFac: 0.5, // Power factor
258 | mnKv: false, // Forcing script to KVS mode (true) or Virtual components mode (false)
259 | }
260 | ```
261 |
262 | ## Updating Script
263 |
264 | > [!WARNING]
265 | > Direct upgrade from script version 4.1 to a newer version is not supported due to a change in KVS data format to JSON.
266 | > After installation, you must reconfigure all settings either in KVS or via Virtual Components.
267 |
268 | 1. Open script from the [Github](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly/blob/master/SmartHeatingWidthShelly.js).
269 | 2. Click the button "Copy raw file". Now the script is in your clipboard memory.
270 |
271 |
272 |
273 | 1. Access the Shelly device web page: Navigate to Settings → Device Information → Device IP → click on the IP address. The Shelly device web page will open; on the left menu, select "<> Scripts."
274 | 2. Open the script you wish to update.
275 | 3. Select all script code and delete it **Ctrl+A** → **Delete**.
276 | 4. Paste the code from the clipboard to the script window **Ctrl+V**.
277 | 5. Save the script, the version is now updated.
278 | 6. All configurations remain unchanged, as they are stored in KVS or Virtual Components.
279 |
280 | ## How to Verify Script Execution
281 |
282 | 1. In Shelly app or web page, navigate to "Schedules".
283 | 2. Inspect the scheduled times when the Shelly will be activated.
284 | 3. Schedulers are organized based on the time.
285 | 4. Advanced users can inspect KVS storage: [Advanced → Key Value Storage → Script Data](#advanced--key-value-storage--script-data)
286 |
287 | ## How the Script Operates
288 |
289 | 1. Internet Connection:
290 | * The script needs the internet to download daily electricity prices and weather forecasts.
291 | 2. Daily Operation:
292 | * It runs every day after 23:00 or as needed during the day to set up heating times.
293 | 3. Workflow:
294 | * The script follows a flowchart to determine the best heating hours based on market prices and weather forecasts.
295 |
296 | ```mermaid
297 | flowchart TD
298 | 0[Start] --> A
299 | A[Get Shelly time and location] --> K{Is forecast used?}
300 | K -- Yes --> B{Get forecast
from Open-Meteo.com API}
301 | B -- Succeeded Calculate heating time --> D{Get market price
from Elering API}
302 | K -- No --> D
303 | B --> M@{ shape: subproc, label: "FailedCheck again in 5 minute" }
304 | D -- SucceededCalculate heating schedules --> L{Check, if market price and forecast needs update}
305 | D --> M
306 | L --> M
307 | L -- YesStart the script --> 0
308 | ```
309 |
310 | 4. Watchdog workflow
311 |
312 | ```mermaid
313 | flowchart TD
314 | 0[Start] --> A
315 | A[Create 'watchdog' event handler] --> K{Is heating script stopped or deleted?}
316 | K -- Yes --> B[Find the script scheduleand delete it]
317 | ```
318 |
319 |
320 | ## Important To Know
321 |
322 | *
When the script is stopped, the schedule is deleted. Shelly only follows the heating algorithm when the script is running.
323 | *Two script instances can run in parallel, however, if the first is in Virtual Components mode, then the second instance can run only in KVS-mode on the same device.
324 | *Up to two instances of this script can run concurrently in KVS mode, both employing different algorithm. These instances can either operate with the same switch output using Shelly Plus 1 or use different switch outputs, as supported by devices like Shelly Plus 2PM.
325 | *This script creates a special "watchdog" script. This "watchdog" script ensures proper cleanup when the heating script is stopped or deleted.
326 | *To mitigate the impact of internet outages, this script uses parameter ``heating time`` to turn on heating based on historically cheap hours.
327 | *The "Run on startup" button for this script must be activated. This setting ensures that the script starts after a power outage, restart, or firmware update.
328 | *This script exclusively handles scheduler generated by its own processes. This script is designed to delete only the scheduler that it has created.
329 | *This solution will only have benefits if you have an hourly priced energy contract. If your energy contract features a flat rate, this solution will not contribute to reducing your energy bill.
330 | * This script depends on the internet and these two services: 331 | * Electricity market price from [Elering API](https://dashboard.elering.ee/assets/api-doc.html#/nps-controller/getPriceUsingGET), 332 | * Weather forecast from [Open-Meteo API](https://open-meteo.com/en/docs). 333 | *The firmware of Shelly Gen2 Plus devices must be version 1.4.4 or higher. The KVS store is read only if the firmware version is 1.4.3 or older. 334 | *
The firmware of Shelly Gen2 Pro or Gen3 devices must be version 1.4.4 or higher. The script will not install Virtual Components if the firmware version is 1.4.3 or older.
335 |
336 |
337 | ## Tested Failure Scenarios
338 | During any of the failures below, Shelly uses the ``Heating Time`` duration to turn on heating based on historically cheap hours.
339 |
340 | In error mode, Shelly divides the heating time bsaed on the configured periods.
341 |
342 | **Failure scenarios:**
343 | 1. Shelly is working, but the internet goes down due to a home router crash or internet provider malfunction. Shelly time continues running.
344 | 2. After a power outage, the internet is not working, and Shelly has no time.
345 | 3. Elering HTTP error occurs, and the Elering server is not reachable.
346 | 4. Elering API failure happens, and the service is down.
347 | 5. Elering API returns incorrect data, and prices are missing.
348 | 6. Weather forecast HTTP error occurs, and the server is unavailable.
349 | 7. Weather forecast API service error occurs, and the JSON data is not received.
350 |
351 | # Smart Heating for Thermia Villa & Eko Classic Using Shelly
352 |
353 | Thermia Villa and Thermia Eko Classic are two old but still widely used ground heating systems.
354 | This guide explains how to make these heat pumps smart using two Shelly devices.
355 |
356 | Step-by-Step Instructions
357 | (Including installer manual screenshots for reference.)
358 |
359 | |Thermia Villa|Thermia Eko Classic|
360 | |---|---|
361 | |
|
|
362 |
363 | Connect **two Shelly devices** inside the heat pump following the **schema below**.
364 |
365 |
366 |
367 | Refer to the table below for **configuring the Shelly devices**.
368 | Both Shelly devices must have Smart Heating script. Configure them according to heating or hot water production.
369 |
370 | |Heatpump|Heating+Hot Water|only Hot Water|
371 | |---|---|---|
372 | |Thermia Villa|Shelly 1
``"InvertedRelay": true``|Shelly 2
``"InvertedRelay": true``|
373 | |Thermia Eko Classic|Shelly 1
``"InvertedRelay": true``|Shelly 2
``"InvertedRelay": false``|
374 |
375 | Below is the **heat pump operating guide** for reference.
376 |
377 | |Thermia Villa|Thermia Eko Classic|Shelly 1|Shelly 2|
378 | |---|---|---|---|
379 | |EVU Stop
No heating or hot water|Hot water
Reduced Temperatur|On|On|
380 | |Hot Water
Reduced Temperatur|EVU Stop
No heating or Hot water|On|Off|
381 | |Normal heating
Normal Hot water|Normal heating
Normal Hot water|Off|On|
382 | |Normal heating
Normal Hot water|Normal heating
Normal Hot water|Off|Off|
383 |
384 | # Smart Heating Algorithms
385 |
386 | ## Weather Forecast Algorithm
387 |
388 | > [!TIP]
389 | > This algorithm calculates the heating time for the next day based on weather forecasts.
390 | > It is particularly effective for various home heating systems. This approach optimizes energy usage by aligning heating needs with anticipated weather conditions.
391 |
392 | ### Advantages of Weather Forecast-Based Heating
393 |
394 | * Temperature Responsiveness:
395 |
396 | When the outside temperature is a mild +17 degrees Celsius, no heating is necessary. Conversely, as the temperature drops to -5 degrees Celsius, there is a need for some heating, and for extremely cold conditions like -20 degrees Celsius, significant amount of heating is required.
397 |
398 | * Smart Heating Management:
399 |
400 | Utilizing weather forecasts allows for smart and adaptive heating management. The system will proactively adjust heating times based on the outside temperature, creating a responsive and dynamic heating schedule.
401 |
402 | * Location-Specific Forecast:
403 |
404 | To provide accurate weather forecasts, location data is necessary. This enables the system to deliver precise predictions for your home's climate, allowing for a customized and effective heating strategy.
405 |
406 | ### Shelly Geolocation
407 |
408 | > [!IMPORTANT]
409 | > Ensure that your Shelly device has the correct location information by checking Shelly → Settings → Geolocation → Latitude/Longitude.
410 |
411 | Note: Shelly's location is determined based on your internet provider's IP address, which may or may not accurately reflect your home location. Verify and update the latitude and longitude settings as needed.
412 |
413 | ### Heating Curve
414 |
415 | The relationship between temperature and heating time is known as the *heating curve*.
416 |
417 | Heating time is influenced by the insulation of your household. For instance, an old and uninsulated house may require 10 hours of heating at -5 degrees, whereas a new A-class house might only need 6 hours.
418 |
419 | To account for these differences, the script includes the parameter ``heatingCurve``, allowing users to customize the heating curve based on their specific household characteristics.
420 |
421 | * 24 hour period graph represents visually how heating time varies with outside temperature and the ``heatingCurve`` parameter which shifts the heating curve to the left or right, whereas shifting 1 equals 1h. The Shelly device has a maximum limit of 20 schedulers, representing the maximum heating hours the script can manage within a 24-hour period. If more heating hours are needed, the script employs a 12-hour algorithm.
422 |
423 |
424 |
425 | ____
426 |
427 | * 12 hour period graph represents visually how heating time varies with outside temperature and the ``heatingCurve`` parameter.
428 |
429 |
430 |
431 | For those interested in the mathematical aspect, the linear equation used to calculate heating time is: ``(Temperature Forecast) * PowerFactor + (Temperature Forecast + heatingCurve)``.
432 |
433 | ## Time Period Algorithm
434 |
435 | > This algorithm divides heating into distinct time periods, activating heating during the most cost-effective hours within each period. It is well-suited for use cases such as hot water boilers, where usage is contingent on the household size rather than external temperature. This method optimizes energy efficiency by aligning heating with periods of lower energy costs.
436 |
437 | * A 24-hour graph with 10 heating hours visually shows when the most affordable times for heating are chosen during the day. The red bar represents heating hours within the day.
438 |
439 |
440 |
441 | ___
442 |
443 | * A 4-hour graph with 1 heating hours visually shows how the most affordable time for heating is chosen during each of the 4h-period. The red bar represents heating hour within the period.
444 |
445 |
446 |
447 |
448 |
449 | # Does it Truly Reduce My Electric Bills
450 | In short: yes.
451 |
452 | Here's a more detailed explanation. While your overall daily electric consumption remains the same, this script optimizes the activation of your heating devices for the most economical hours. Consequently, even with the same energy consumption, your electricity bill is reduced.
453 |
454 | Appliances like water heaters, water tanks, ground-source or air-source heat pumps, electric radiators, underfloor electric heaters, and air conditioning are examples of energy-intensive devices that benefit from being activated during the most cost-effective times of the day.
455 |
456 | Electricity prices can fluctuate significantly, sometimes varying up to 100 times during a day. Check electricity market prices for more information. https://dashboard.elering.ee/et/nps/price
457 |
458 | # Troubleshooting
459 |
460 | ## Error "Couldn't get script"
461 |
462 | There is an issue within the Shelly system that may affect your experience when attempting to open scripts through the Shelly cloud or mobile app. The encountered error, "Couldn't get script," is a known bug preventing the opening of scripts larger than 15kB via these platforms.
463 |
464 | To navigate around this inconvenience, we suggest the following workarounds:
465 |
466 | 1. Open the Script Through Device Web Page:
467 | Access the device web page to successfully open any script. This method provides a direct and reliable solution to view and manage your scripts seamlessly.
468 |
469 | 2. Alternative Solution Through Shelly Cloud:
470 | If accessing the device web page is not feasible, follow these steps on the Shelly cloud:
471 |
472 | 1. Delete the existing script.
473 | 2. Create a new script.
474 | 3. Copy and paste the entire script into the scripting window.
475 | 4. Configure all necessary settings.
476 | 5. Save and close the script.
477 | 6. Run the script.
478 |
479 | If any issues arise during this process, you can repeat the workaround by starting from the script deletion step.
480 |
481 |
482 |
483 | ## Advanced → Key Value Storage → Script Data
484 |
485 | The script saves data in Shelly KVS (Key-Value-Storage) to preserve it in case of power outages or restarts.
486 |
487 | To access the stored data on the Shelly device web page, navigate to **Advanced → KVS**.
488 |
489 | 2. Key: ``LastCalculation`` Value: ``Fri Dec 27 2024 23:29:20 GMT+0200``
490 |
491 | This timestamp indicates the time when the script successfully retrieved market prices from Elering and created schedules. While this information is primarily for your reference, it offers insights into the timeline of script activities.
492 |
493 | 1. Key: ``ExistingSchedule`` Value: ``1``
494 |
495 | The numeric values represent schedule ID number created by the script. This information is crucial for each script to identify and manage schedule associated with it. It aids in the proper deletion of outdated schedules when creating new ones is necessary.
496 |
497 | 3. Key: ``Version`` Value: ``4.3``
498 |
499 | The version indicates the installed script version.
500 |
501 |
502 |
503 | # License
504 |
505 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
506 |
507 | # Author
508 |
509 | Created by Leivo Sepp, 2024-2025
510 |
511 | [Smart heating management with Shelly - GitHub Repository](https://github.com/LeivoSepp/Smart-heating-management-with-Shelly)
--------------------------------------------------------------------------------
/SmartHeatingWidthShelly.js:
--------------------------------------------------------------------------------
1 | /*
2 | Created by Leivo Sepp, 2024-2025
3 | Licensed under the MIT License
4 | https://github.com/LeivoSepp/Smart-heating-management-with-Shelly
5 |
6 | This Shelly script is designed to retrieve energy market prices from Elering and
7 | activate heating during the most cost-effective hours each day, employing various algorithms.
8 |
9 | 1. Dynamic calculation of heating time for the next day based on weather forecasts.
10 | 2. Division of heating into time periods, with activation during the cheapest hour within each period.
11 | 3. Utilization of min-max price levels to maintain the Shelly system consistently on or off.
12 | The script executes daily after 23:00 to establish heating timeslots for the following day.
13 | */
14 |
15 | /* Electricity transmission fees (EUR/MWh) excluding VAT.
16 | Elektrilevi https://elektrilevi.ee/en/vorguleping/vorgupaketid/eramu
17 | Imatra https://imatraelekter.ee/vorguteenus/vorguteenuse-hinnakirjad/
18 | Latvia https://sadalestikls.lv/en/tarifi
19 | */
20 | function pack() {
21 | return {
22 | VORK1: { dRt: 77.2, nRt: 77.2, dMRt: 77.2, hMRt: 77.2 },
23 | VORK2: { dRt: 60.7, nRt: 35.1, dMRt: 60.7, hMRt: 35.1 },
24 | VORK4: { dRt: 36.9, nRt: 21, dMRt: 36.9, hMRt: 21 },
25 | VORK5: { dRt: 52.9, nRt: 30.3, dMRt: 81.8, hMRt: 47.4 },
26 | PARTN24: { dRt: 60.7, nRt: 60.7, dMRt: 60.7, hMRt: 60.7 },
27 | PARTN24PL: { dRt: 38.6, nRt: 38.6, dMRt: 38.6, hMRt: 38.6 },
28 | PARTN12: { dRt: 72.4, nRt: 42, dMRt: 72.4, hMRt: 42 },
29 | PARTN12PL: { dRt: 46.4, nRt: 27.1, dMRt: 46.4, hMRt: 27.1 },
30 | PAMATA1: { dRt: 39.62, nRt: 39.62, dMRt: 39.62, hMRt: 39.62 },
31 | SPECIAL1: { dRt: 158.48, nRt: 158.48, dMRt: 158.48, hMRt: 158.48 },
32 | NONE: { dRt: 0, nRt: 0, dMRt: 0, hMRt: 0 },
33 | }
34 | }
35 | /****** INITIAL SETTINGS ******/
36 | /*
37 | After the initial run, all user settings are stored in the Shelly 1) KVS or 2) Virtual components (in case virtual components are supported).
38 | To modify user settings, you’ll need to access the Shelly KVS via: Menu → Advanced → KVS on the Shelly web page.
39 | Once you’ve updated the settings, restart the script to apply the changes or wait for the next scheduled run.
40 |
41 | timePeriod: Heating Period is the time during which heating time is calculated. (0 -> only min-max price used, 24 -> period is one day).
42 | heatingTime: Heating Time is the duration of the cheapest hours within a Heating Period when the heating system is activated. or duration of heating in a day in case of internet connection failure.
43 | isFcstUsed: true/false - Using weather forecast to calculate heating duration.
44 | */
45 | let c = {
46 | tPer: 24, // KVS:TimePeriod VC:Heating Period (h) 24/12/6/0
47 | hTim: 10, // KVS:HeatingTime VC:Heating Time (h/period)
48 | isFc: false, // KVS:IsForecastUsed VC:Forecast Heat
49 | pack: "VORK2", // KVS:EnergyProvider VC:Network Package (NONE, VORK1, VORK2, VORK4, VORK5, PARTN24, PARTN24PL, PARTN12, PARTN12PL, PAMATA1, SPECIAL1)
50 | lowR: 1, // KVS:AlwaysOnPrice VC:Heat On (min price) (EUR/MWh)
51 | higR: 300, // KVS:AlwaysOffPrice VC:Heat Off (max price) (EUR/MWh)
52 | Inv: false, // KVS:InvertedRelay VC:Inverted Relay
53 | rId: 0, // KVS:RelayId VC: N/A, always first relay (0)
54 | cnty: "ee", // KVS:Country VC:Market Price Country (ee, fi, lv, lt)
55 | hCur: 0, // KVS:HeatingCurve VC:Heating Curve
56 | tmr: 60, // Default timer
57 | pFac: 0.5, // Power factor
58 | mnKv: false, // Forcing script to KVS mode (true) or Virtual components mode (false)
59 | }
60 | /****** PROGRAM INITIAL SETTINGS ******/
61 |
62 | let s = {
63 | last: 0, // KVS:LastCalculation Last calculation timestamp
64 | exSc: 0, // KVS:ExistingSchedule Existing heating schedule
65 | vers: 0, // KVS:Version
66 | }
67 |
68 | /*
69 | Heating time dependency on heating curve and outside temperature for 24h and 12h (power factor 0.5).
70 |
71 | | ------ 24h heating curve ------ |
72 | °C |-10 -8 -6 -4 -2 0 2 4 6 8 10
73 | _________________________________________________
74 | 17 | 0 0 0 0 0 0 0 0 0 0 0
75 | 15 | 0 0 0 0 0 0 0 2 4 6 8
76 | 10 | 0 0 0 0 0 1 3 5 7 9 11
77 | 5 | 0 0 0 0 1 3 5 7 9 11 13
78 | 0 | 0 0 0 2 4 6 8 10 12 14 16
79 | -5 | 0 0 2 4 6 8 10 12 14 16 18
80 | -10 | 1 3 5 7 9 11 13 15 17 19 21
81 | -15 | 3 5 7 9 11 13 15 17 19 21 23
82 | -20 | 6 8 10 12 14 16 18 20 22 24 24
83 | -25 | 8 10 12 14 16 18 20 22 24 24 24
84 |
85 | | ------- 12h heating curve ------- |
86 | °C |-10 -8 -6 -4 -2 0 2 4 6 8 10
87 | _________________________________________________
88 | 17 | 0 0 0 0 0 0 0 0 0 0 0
89 | 15 | 0 0 0 0 0 0 0 1 2 3 4
90 | 10 | 0 0 0 0 0 1 2 3 4 5 6
91 | 5 | 0 0 0 0 1 2 3 4 5 6 7
92 | 0 | 0 0 0 1 2 3 4 5 6 7 8
93 | -5 | 0 0 1 2 3 4 5 6 7 8 9
94 | -10 | 1 2 3 4 5 6 7 8 9 10 11
95 | -15 | 2 3 4 5 6 7 8 9 10 11 12
96 | -20 | 3 4 5 6 7 8 9 10 11 12 12
97 | -25 | 4 5 6 7 8 9 10 11 12 12 12
98 |
99 | Forecast temp °C is "feels like": more information here: https://en.wikipedia.org/wiki/Apparent_temperature
100 | */
101 |
102 | let _ = {
103 | hTim: 0, //heating time
104 | cPer: 0, //number of periods
105 | tsPr: '', //timestamp for prices
106 | tsFc: '', //timestamp for forecast
107 | freq: 300, //frequency of script execution in seconds (5 min)
108 | isLp: false, //loop flag
109 | updD: Math.floor(Math.random() * 46), //delay for server requests (max 45min)
110 | sId: Shelly.getCurrentScriptId(), //script ID
111 | pId: "Id" + Shelly.getCurrentScriptId() + ": ", //print ID
112 | scId: '', //schedule ID
113 | manu: false, //manual heating flag
114 | prov: "None", //network provider name
115 | newV: 4.9, //new script version
116 | sdOk: false, //system data OK
117 | cdOk: false, //configuration data OK
118 | };
119 | let cntr = 0; //counter for async functions
120 |
121 | function dtVc() {
122 | return [
123 | {
124 | type: "group", id: 200, config: {
125 | name: "Smart Heating"
126 | }
127 | },
128 | {
129 | type: "enum", id: 200, config: {
130 | name: "Heating Period (h)",
131 | options: ["24", "12", "6", "0"],
132 | default_value: "24",
133 | persisted: true,
134 | meta: { ui: { view: "dropdown", webIcon: 13, titles: { "24": "24 hour", "12": "12 hour", "6": "6 hour", "0": "No period" } } }
135 | }
136 | },
137 | {
138 | type: "number", id: 200, config: {
139 | name: "Min On Time (h/period)",
140 | default_value: 10,
141 | min: 0,
142 | max: 24,
143 | persisted: true,
144 | meta: { ui: { view: "slider", unit: "h/period" } }
145 | }
146 | },
147 | {
148 | type: "enum", id: 201, config: {
149 | name: "Network Package",
150 | options: ["NONE", "VORK1", "VORK2", "VORK4", "VORK5", "PARTN24", "PARTN24PL", "PARTN12", "PARTN12PL", "PAMATA1", "SPECIAL1"],
151 | default_value: "VORK2",
152 | persisted: true,
153 | meta: { ui: { view: "dropdown", webIcon: 22, titles: { "NONE": "No package", "VORK1": "Võrk1 Base", "VORK2": "Võrk2 DayNight", "VORK4": "Võrk4 DayNight", "VORK5": "Võrk5 DayNightPeak", "PARTN24": "Partner24 Base", "PARTN24PL": "Partner24Plus Base", "PARTN12": "Partner12 DayNight", "PARTN12PL": "Partner12Plus DayNight", "PAMATA1": "Pamata-1", "SPECIAL1": "Speciālais 1" } } }
154 | }
155 | },
156 | {
157 | type: "number", id: 201, config: {
158 | name: "Heat On (min price)",
159 | default_value: 1,
160 | min: 0,
161 | max: 100,
162 | persisted: true,
163 | meta: { ui: { view: "slider", unit: "€/MWh or less" } }
164 | }
165 | },
166 | {
167 | type: "number", id: 202, config: {
168 | name: "Heat Off (max price)",
169 | default_value: 300,
170 | min: 0,
171 | max: 500,
172 | persisted: true,
173 | meta: { ui: { view: "slider", unit: "€/MWh or more" } }
174 | }
175 | },
176 | {
177 | type: "boolean", id: 201, config: {
178 | name: "Inverted Relay",
179 | default_value: false,
180 | persisted: true,
181 | meta: { ui: { view: "toggle", webIcon: 7, titles: ["No", "Yes"] } }
182 | }
183 | },
184 | {
185 | type: "enum", id: 202, config: {
186 | name: "Market Price Country",
187 | options: ["ee", "fi", "lv", "lt"],
188 | default_value: "ee",
189 | persisted: true,
190 | meta: { ui: { view: "dropdown", webIcon: 9, titles: { "ee": "Estonia", "fi": "Finland", "lv": "Latvia", "lt": "Lithuania" } } }
191 | }
192 | },
193 | {
194 | type: "boolean", id: 200, config: {
195 | name: "Forecast Heat",
196 | default_value: false,
197 | persisted: true,
198 | meta: { ui: { view: "toggle", webIcon: 14, titles: ["No", "Yes"] } }
199 | }
200 | },
201 | {
202 | type: "number", id: 203, config: {
203 | name: "Forecast Impact +/-",
204 | default_value: 0,
205 | min: -4,
206 | max: 8,
207 | persisted: true,
208 | meta: { ui: { view: "slider", unit: "h more heat" } }
209 | }
210 | },
211 | ];
212 | }
213 |
214 | function strt() {
215 | sAut();
216 | gKvs();
217 | }
218 | /* set the script to sart automatically on boot */
219 | function sAut() {
220 | if (!Shelly.getComponentConfig("script", _.sId).enable) {
221 | Shelly.call('Script.SetConfig', { id: _.sId, config: { enable: true } },
222 | function (res, err, msg) {
223 | if (err != 0) {
224 | print(_.pId, "Heating script autostart is not enabled.", msg);
225 | }
226 | });
227 | }
228 | }
229 |
230 | // check if Shelly supports Virtual components
231 | function isVC() {
232 | const info = Shelly.getDeviceInfo();
233 |
234 | if (c.mnKv === true) {
235 | print(_.pId, "ManualKVS=true → forcing KVS mode");
236 | return false;
237 | }
238 |
239 | // Gen4 ja Gen3 OK; Gen2 ainult Pro + min FW 1.4.3
240 | const gen2ok = (info.gen === 2 && (info.app || "").substring(0, 3) === "Pro" && verC('1.4.3', info.ver));
241 | return (info.gen === 4 || info.gen === 3 || gen2ok);
242 | }
243 | // compare Shelly FW versions
244 | function verC(old, newV) {
245 | const oldP = old.split('.');
246 | const newP = newV.split('.');
247 | for (var i = 0; i < newP.length; i++) {
248 | let a = ~~newP[i]; // parse int
249 | let b = ~~oldP[i]; // parse int
250 | if (a > b) return true;
251 | if (a < b) return false;
252 | }
253 | return false;
254 | }
255 | // Get KVS ConfigurationData into memory
256 | function memC(dt) {
257 | c.tPer = dt.TimePeriod;
258 | c.hTim = dt.HeatingTime;
259 | c.isFc = dt.IsForecastUsed;
260 | c.pack = dt.EnergyProvider;
261 | c.lowR = dt.AlwaysOnPrice;
262 | c.higR = dt.AlwaysOffPrice;
263 | c.Inv = dt.InvertedRelay;
264 | c.rId = dt.RelayId;
265 | c.cnty = dt.Country;
266 | c.hCur = dt.HeatingCurve;
267 | c.mnKv = typeof dt.ManualKVS === "boolean" ? dt.ManualKVS : c.mnKv;
268 | return c;
269 | }
270 | // ConfigurationData data to KVS store
271 | function kvsC() {
272 | let cdat = {};
273 | cdat.TimePeriod = c.tPer;
274 | cdat.HeatingTime = c.hTim;
275 | cdat.IsForecastUsed = c.isFc;
276 | cdat.EnergyProvider = c.pack;
277 | cdat.AlwaysOnPrice = c.lowR;
278 | cdat.AlwaysOffPrice = c.higR;
279 | cdat.InvertedRelay = c.Inv;
280 | cdat.RelayId = c.rId;
281 | cdat.Country = c.cnty;
282 | cdat.HeatingCurve = c.hCur;
283 | cdat.ManualKVS = c.mnKv;
284 | return cdat;
285 | }
286 | // Get KVS SystemData into memory
287 | function memS(dt) {
288 | s.exSc = dt.ExistingSchedule;
289 | s.vers = dt.Version;
290 | return s;
291 | }
292 | // SystemData data to KVS store
293 | function kvsS() {
294 | let sdat = {};
295 | sdat.LastCalculation = s.last;
296 | sdat.ExistingSchedule = s.exSc;
297 | sdat.Version = s.vers;
298 | return sdat;
299 | }
300 | // Get KVS ConfigurationData and SystemData
301 | function gKvs() {
302 | cntr = 2;
303 | Shelly.call('KVS.Get', { key: "SmartHeatingConf" + _.sId },
304 | function (res, err) {
305 | cntr--;
306 | if (err !== 0) {
307 | // Failed to get ConfigurationData
308 | return;
309 | }
310 | c = memC(JSON.parse(res.value));
311 | _.cdOk = true;
312 | });
313 |
314 | Shelly.call('KVS.Get', { key: "SmartHeatingSys" + _.sId },
315 | function (res, err) {
316 | cntr--;
317 | if (err !== 0) {
318 | // Failed to get SystemData
319 | return;
320 | }
321 | s = memS(JSON.parse(res.value));
322 | _.sdOk = true;
323 | });
324 | wait(inst);
325 | }
326 |
327 | // Select running mode like KVS or Virtual components
328 | function inst() {
329 | if (isVC()) {
330 | if (_.sdOk && !(s.vers < 4.2)) {
331 | print(_.pId, "Existing Virtual Component mode");
332 | rVc();
333 | } else {
334 | print(_.pId, "New Virtual Component installation");
335 | gVc();
336 | }
337 | } else {
338 | print(_.pId, "Script in KVS mode");
339 | tKvs();
340 | }
341 | }
342 |
343 | // Store configuration data to KVS
344 | function tKvs() {
345 | Shelly.call("KVS.set", { key: "SmartHeatingConf" + _.sId, value: JSON.stringify(kvsC()) },
346 | function (res, err, msg) {
347 | if (err !== 0) {
348 | console.log(_.pId, "Configuration not stored in KVS:", err, msg);
349 | } else {
350 | console.log(_.pId, "Configuration settings stored in KVS");
351 | }
352 | }
353 | );
354 | main();
355 | }
356 |
357 | // Get all virtual components and delete them all before new installation
358 | function gVc() {
359 | Shelly.call("Shelly.GetComponents", { dynamic_only: true, include: ["status"] }, function (res, err, msg) {
360 | if (err === 0) {
361 | if (res.components && res.components.length > 0) {
362 | dVc(res.components); // Delete all Virtual Components
363 | } else {
364 | aVc(dtVc()); // Add VCom and pass Virtual Components data
365 | }
366 | } else {
367 | print(_.pId, "Failed to get virtual components: " + msg);
368 | }
369 | });
370 | }
371 |
372 | // Delete all virtual components for new installation only
373 | function dVc(vCom) {
374 | if (cntr < 6 - 1) {
375 | for (let i = 0; i < 1 && i < vCom.length; i++) {
376 | let key = vCom.splice(0, 1)[0].key;
377 | cntr++;
378 | Shelly.call("Virtual.Delete", { key: key },
379 | function (res, err, msg) {
380 | if (err === 0) {
381 | print(_.pId, "Clean Virtual Components");
382 | } else {
383 | print(_.pId, "Virtual component is not deleted: " + msg);
384 | }
385 | cntr--;
386 | }
387 | );
388 | }
389 | }
390 | if (vCom.length > 0) {
391 | Timer.set(1000, false, dVc, vCom);
392 | } else {
393 | wait([aVc, dtVc()]);
394 | }
395 | }
396 |
397 | // Add all new virtual components
398 | function aVc(vCom) {
399 | if (cntr < 6 - 1) {
400 | for (let i = 0; i < 1 && i < vCom.length; i++) {
401 | let comp = vCom.splice(0, 1)[0];
402 | cntr++;
403 | Shelly.call("Virtual.Add", { type: comp.type, id: comp.id, config: comp.config },
404 | function (res, err, msg) {
405 | if (err === 0) {
406 | print(_.pId, "Added new virtual component: " + res.id);
407 | } else {
408 | print(_.pId, "Virtual component is not added: " + msg);
409 | }
410 | cntr--;
411 | }
412 | );
413 | }
414 | }
415 | if (vCom.length > 0) {
416 | Timer.set(1000, false, aVc, vCom);
417 | } else {
418 | wait(sGrp);
419 | }
420 | }
421 |
422 | // Add virtual components to group
423 | function sGrp() {
424 | let gCnf = {
425 | id: 200,
426 | value: [
427 | "enum:200",
428 | "number:200",
429 | "boolean:200",
430 | "number:203",
431 | "enum:201",
432 | "number:201",
433 | "number:202",
434 | "boolean:201",
435 | "enum:202"
436 | ]
437 | };
438 | Shelly.call("Group.Set", gCnf, function (res, err, msg) {
439 | if (err !== 0) {
440 | print(_.pId, "Group config is not set: " + msg);
441 | }
442 | });
443 | rVc();
444 | }
445 |
446 | // Read all virtual components and store the values to memory
447 | function rVc() {
448 | cntr++;
449 | let mpVC = [
450 | ["tPer", "enum:200"],
451 | ["hTim", "number:200"],
452 | ["isFc", "boolean:200"],
453 | ["pack", "enum:201"],
454 | ["lowR", "number:201"],
455 | ["higR", "number:202"],
456 | ["Inv", "boolean:201"],
457 | ["cnty", "enum:202"],
458 | ["hCur", "number:203"],
459 | ];
460 | Shelly.call("Shelly.GetComponents", { dynamic_only: true, include: ["status"] },
461 | function (res, err) {
462 | if (err === 0) {
463 | let comp = res.components;
464 | res = null;
465 | if (comp && comp.length > 0) {
466 | for (let i = 0; i < mpVC.length; i++) {
467 | for (let j = 0; j < comp.length; j++) {
468 | if (mpVC[i][1] === comp[j].key) {
469 | c[mpVC[i][0]] = comp[j].status.value;
470 | break;
471 | }
472 | }
473 | }
474 |
475 | }
476 | }
477 | cntr--;
478 | });
479 | wait(main);
480 | }
481 |
482 | // Main script where all the logic starts.
483 | function main() {
484 | _.cPer = c.tPer <= 0 ? 0 : Math.ceil((24 * 100) / (c.tPer * 100)); //number of periods in a day
485 | _.hTim = c.hTim > c.tPer ? c.tPer : c.hTim; //heating time can't be more than the period
486 | //check if Shelly has time
487 | if (!isTm) {
488 | hErr("Shelly has no time");
489 | return;
490 | }
491 | // set the network provider
492 | if (c.pack.substring(0, 4) == "VORK") {
493 | _.prov = "Elektlevi";
494 | } else if (c.pack.substring(0, 4) == "PART") {
495 | _.prov = "Imatra";
496 | } else if (c.pack.substring(0, 4) == "PAMA" || c.pack.substring(0, 7) == "SPECIAL") {
497 | _.prov = "Lv";
498 | }
499 | print(_.pId, "Network provider: ", _.prov, c.pack);
500 |
501 |
502 | // If weather forecast is used for heating hours
503 | if (c.isFc && c.tPer > 0) {
504 | gFcs();
505 | } else {
506 | gEle();
507 | }
508 | }
509 |
510 | // Get Open-Meteo min and max "feels like" temperatures
511 | function gFcs() {
512 | const lat = JSON.stringify(Shelly.getComponentConfig("sys").location.lat);
513 | const lon = JSON.stringify(Shelly.getComponentConfig("sys").location.lon);
514 | let url = "https://api.open-meteo.com/v1/forecast?hourly=apparent_temperature&timezone=auto&forecast_days=1&forecast_hours=";
515 | url = url + c.tPer + "&latitude=" + lat + "&longitude=" + lon;
516 | print(_.pId, "Forecast query: ", url)
517 | Shelly.call("HTTP.GET", { url: url, timeout: 5, ssl_ca: "*" }, function (res, err) {
518 | url = null;
519 | if (err != 0 || res === null || res.code != 200) {
520 | hErr("Get forecast HTTP.GET error, check again in " + _.freq / 60 + " min.");
521 | return;
522 | }
523 | //open-meteo json response to get 6h, 12h or 24h temperatures
524 | const temp = JSON.parse(res.body)["hourly"]["apparent_temperature"];
525 | res = null;
526 | let sumT = 0;
527 | for (let i = 0; i < temp.length; i++) {
528 | sumT += temp[i];
529 | }
530 |
531 | const tFcs = Math.ceil(sumT / c.tPer); //AVG and round temperature up
532 | _.tsFc = Math.floor(Date.now() / 1000.0); //store the timestamp into memory
533 | print(_.pId, "We got weather forecast from Open Meteo at ", new Date().toString());
534 |
535 | // calculating heating hours
536 | const maxT = 16; //max temperature for the forecast
537 | let fcTm = ((maxT - tFcs) * (c.pFac - 1) + (maxT - tFcs + c.hCur * 2 - 2)); //the main heating time calculation algorithm
538 | fcTm = fcTm < 0 || tFcs > maxT ? 0 : fcTm; //heating time can't be negative
539 | _.hTim = Math.floor(fcTm / _.cPer); //heating time per period (round-down heating time)
540 | _.hTim = _.hTim > c.tPer ? c.tPer : _.hTim; //heating time can't be more than the period
541 | // _.hTim = _.hTim < c.hTim ? c.hTim : _.hTim; //heating time can't be less than the user setting
542 |
543 | print(_.pId, "Temperture forecast width windchill is ", tFcs, " °C, and heating enabled for ", _.hTim, " hours.");
544 | gEle();
545 | });
546 | }
547 | // Get electricity market price CSV file from Elering
548 | function gEle() {
549 | // set the date range for Elering query
550 | const epch = Shelly.getComponentStatus("sys").unixtime;
551 | const shHr = new Date(epch * 1000).getHours();
552 | // After 23:00 tomorrow's energy prices are used
553 | // before 23:00 today's energy prices are used.
554 | const addD = shHr >= 23 ? 0 : -1;
555 | const isoT = new Date((epch + gTz() + 60 * 60 * 24 * addD) * 1000).toISOString().slice(0, 10);
556 | const isoN = new Date((epch + gTz() + (60 * 60 * 24 * (addD + 1))) * 1000).toISOString().slice(0, 10);
557 | const dtSt = isoT + "T" + (24 - gTz() / 3600) + ":00Z";
558 | const dtEn = isoN + "T" + (24 - gTz() / 3600) + ":00Z";
559 | // Build Elering URL
560 | let url = "https://dashboard.elering.ee/api/nps/price/csv?fields=";
561 | url += c.cnty + "&start=" + dtSt + "&end=" + dtEn;
562 | print(_.pId, "Elering query: ", url);
563 |
564 | Shelly.call("HTTP.GET", { url: url, timeout: 5, ssl_ca: "*" }, function (res, err) {
565 | url = null;
566 | if (err != 0 || res === null || res.code != 200 || !res.body_b64) {
567 | hErr("Elering HTTP.GET error, check again in " + _.freq / 60 + " min.");
568 | return;
569 | }
570 | c.pack = eval("pack()." + c.pack); //convert transfer fee to variable and load the data
571 |
572 | res.body_b64 = atob(res.body_b64); //decode base64 to text
573 | let body = res.body_b64.substring(res.body_b64.indexOf("\n") + 1); //skip the first line
574 | res = null;
575 | let raw = [];
576 | let eler = [];
577 | let aPos = 0;
578 | while (aPos >= 0) {
579 | body = body.substring(aPos);
580 | aPos = 0;
581 | let row = [0, 0];
582 | aPos = body.indexOf("\"", aPos) + 1;
583 | if (aPos === 0) {
584 | break; // EOF
585 | }
586 | // Epoch
587 | row[0] = Number(body.substring(aPos, body.indexOf("\"", aPos)));
588 | let pric = 0;
589 | let hr = new Date(row[0] * 1000).getHours();
590 | let hr15 = hr;
591 | let avg = 0;
592 | while (hr === hr15 && hr15 < 24) //sum 1 hour prices
593 | {
594 | avg++;
595 | aPos = body.indexOf(";\"", aPos) + 2; //skip ;
596 | aPos = body.indexOf(";\"", aPos) + 2; //find price
597 | pric += Number(body.substring(aPos, body.indexOf("\"", aPos)).replace(",", "."));
598 |
599 | aPos = body.indexOf("\n", aPos); //next line
600 | let nxt = body.indexOf("\"", aPos) + 1; //next epoch
601 | if (nxt === 0) {
602 | break; // EOF
603 | }
604 | hr15 = new Date(Number(body.substring(nxt, body.indexOf("\"", nxt))) * 1000).getHours(); //next hour
605 | }
606 |
607 | row[1] = Math.round((pric / avg) * 100) / 100; //avg price for the hour, round 2 dec places
608 | row[1] += fFee(row[0]); //add transfer fee
609 | raw.push(row);
610 | }
611 | //if elering API returns less than 24 rows, the script will try to download the data again after set of minutes
612 | if (raw.length < 24) {
613 | hErr("Elering API didn't return prices, check again in " + _.freq / 60 + " min.");
614 | return;
615 | }
616 | //store the timestamp into memory
617 | _.tsPr = Math.floor(Date.now() / 1000.0);
618 | print(_.pId, "We got market prices from Elering ", new Date().toString());
619 |
620 | if (c.tPer <= 0) {
621 | // Calculate schedules based on alwaysOnLowPrice.
622 | for (let a = 0; a < raw.length; a++) {
623 | let ts = raw[a][0];
624 | let pric = raw[a][1];
625 | let fee = fFee(ts);
626 | if (pric - fee < c.lowR) { //if price - transferFee is less than min price
627 | eler.push([new Date(ts * 1000).getHours(), pric]);
628 | print(_.pId, "Energy price ", pric - fee, " EUR/MWh at ", new Date(ts * 1000).getHours() + ":00 is less than min price and used for heating.");
629 | }
630 | }
631 | if (!eler.length) {
632 | print(_.pId, "No energy prices below min price level. No heating.");
633 | }
634 | } else { // Calculate schedules based on the cheap hours in the heating period.
635 | let numP = Math.ceil((new Date().getHours() % 23 + 2) / c.tPer); //finds the current period for forecast calculation
636 |
637 | // Create an array for each heating period, sort, and push the prices
638 | for (let i = 0; i < _.cPer; i++) { //loop through the periods
639 | if (c.isFc && (i + 1) != numP) { continue; } //use only the current period in case of forecast, skip the rest
640 | let hPer = (i + 1) * c.tPer > 24 ? 24 : (i + 1) * c.tPer; //finds the end of the period
641 | let oneP = [];
642 | for (let j = i * c.tPer; j < hPer; j++) { //finds the prices in the period
643 | oneP.push(raw[j]); //copy the price to the new array
644 | }
645 | oneP = srAr(oneP, 1); //sort by price
646 | let hHrs = oneP.length < _.hTim ? oneP.length : _.hTim; //finds max hours to heat in that period
647 |
648 | for (let a = 0; a < oneP.length; a++) {
649 | let ts = oneP[a][0];
650 | let pric = oneP[a][1];
651 | let fee = fFee(ts);
652 | if ((a < hHrs || pric - fee < c.lowR) && !(pric - fee > c.higR)) {
653 | eler.push([new Date((ts) * 1000).getHours(), pric]);
654 | }
655 | }
656 | }
657 | if (!eler.length) {
658 | print(_.pId, "Current configuration does not permit heating during any hours; it is likely that the AlwaysOffPrice value is set too low.")
659 | }
660 | }
661 | c.pack, raw = null;
662 | _.manu = false;
663 | fTmr(); //set default timer
664 | fdSc(eler); //delete existing schedule and pass eler data to create schedule
665 | eler = null;
666 | });
667 | }
668 |
669 | // Get Shelly timezone offset in seconds
670 | function gTz() {
671 | const shDt = new Date(Shelly.getComponentStatus("sys").unixtime * 1000);
672 | const shHr = shDt.getHours();
673 | const utcH = shDt.toISOString().slice(11, 13); //UTC hour
674 | let tz = shHr - utcH; //timezone offset
675 | if (tz > 12) { tz -= 24; }
676 | if (tz < -12) { tz += 24; }
677 | return tz * 60 * 60;
678 | }
679 |
680 | // Calculate transfer fee based on the timestamp.
681 | function fFee(epoch) {
682 | const hour = new Date(epoch * 1000).getHours();
683 | const day = new Date(epoch * 1000).getDay();
684 | const mnth = new Date(epoch * 1000).getMonth();
685 | if (_.prov === "Elektlevi") {
686 | if ((mnth >= 10 || mnth <= 2) && (day === 0 || day === 6) && hour >= 16 && hour < 20) {
687 | // peak holiday: Nov-Mar, SA-SU at 16:00–20:00
688 | return c.pack.hMRt;
689 | } else if ((mnth >= 10 || mnth <= 2) && ((hour >= 9 && hour < 12) || (hour >= 16 && hour < 20))) {
690 | // peak daytime: Nov-Mar: MO-FR at 09:00–12:00 and at 16:00–20:00
691 | return c.pack.dMRt;
692 | } else if (hour < 7 || hour >= 22 || day === 6 || day === 0) {
693 | //night-time: MO-FR at 22:00–07:00, SA-SU all day
694 | return c.pack.nRt;
695 | } else {
696 | //daytime: MO-FR at 07:00–22:00
697 | return c.pack.dRt;
698 | }
699 | } else if (_.prov === "Imatra") {
700 | if (gTz() / 60 / 60 === 3) { //summer time
701 | if (hour < 8 || day === 6 || day === 0) {
702 | //summer-night-time: MO-FR at 00:00–08:00, SA-SU all day
703 | return c.pack.nRt;
704 | } else {
705 | //daytime: MO-FR at 08:00–24:00
706 | return c.pack.dRt;
707 | }
708 | } else {
709 | if (hour < 7 || hour >= 23 || day === 6 || day === 0) {
710 | //winter-night-time: MO-FR at 23:00–07:00, SA-SU all day
711 | return c.pack.nRt;
712 | } else {
713 | //daytime: MO-FR at 07:00–23:00
714 | return c.pack.dRt;
715 | }
716 | }
717 | } else if (_.prov === "Lv") {
718 | return c.pack.dRt;
719 | } else {
720 | return 0;
721 | }
722 | }
723 |
724 | // Set countdown timer to flip Shelly status
725 | function fTmr() {
726 | const timr = c.tmr * 60 + 10; //+10sec to remove flap between continous heating hours
727 | Shelly.call("Switch.SetConfig", {
728 | id: c.rId,
729 | config: {
730 | auto_on: c.Inv,
731 | auto_on_delay: timr,
732 | auto_off: !c.Inv,
733 | auto_off_delay: timr
734 | }
735 | });
736 | }
737 | // Delete the existing schedule if it exists
738 | function fdSc(eler) {
739 | cntr = 1;
740 | Shelly.call("Schedule.Delete", { id: s.exSc }, function () {
741 | cntr--;
742 | });
743 | wait([fScd, eler]);
744 | }
745 |
746 | // Create a new schedule with the advanced timespec to cover all the hours within the same schedule item
747 | function fScd(eler) {
748 | cntr = 1;
749 | if (eler === undefined || eler.length == 0) {
750 | print(_.pId, "No heating calculated for any hours with the current configuration.")
751 | fKvs();
752 | return;
753 | }
754 | // Sort the heating by hour
755 | let sArr = srAr(eler, 0);
756 | eler = [];
757 | _.scId = 0;
758 | let hArr = [];
759 | let pArr = [];
760 | for (let i = 0; i < sArr.length; i++) {
761 | let hr = sArr[i][0];
762 | hArr.push(hr);
763 | let t = hr < 10 ? "0" + hr : hr;
764 | pArr.push(t + ":00 (" + sArr[i][1] + ")");
765 | }
766 | const hrs = hArr.join(","); //create timespec
767 | const pric = pArr.join(", "); //create hours (prices) for print
768 | Shelly.call("Schedule.Create", {
769 | enable: true,
770 | timespec: "0 0 " + hrs + " * * *",
771 | calls: [{
772 | method: "Switch.Set",
773 | params: {
774 | id: c.rId,
775 | on: !c.Inv
776 | }
777 | }]
778 | }, function (res, err, msg) {
779 | if (err !== 0) {
780 | print(_.pId, "Scheduler not created:", err, msg);
781 | } else {
782 | _.scId = res.id; //last scheduleID to store in KVS
783 | }
784 | cntr--;
785 | });
786 | print(_.pId, "Heating will be turned on to following hours 'HH:mm (EUR/MWh Energy Price + Transmission)':\n", pric);
787 | wait(fKvs);
788 | }
789 |
790 | // Store the schedulerID, version and last calculation to KVS to have them in case of power outage
791 | function fKvs() {
792 | cntr = 1;
793 | s.last = new Date().toString();
794 | s.exSc = _.scId;
795 | s.vers = _.newV;
796 | Shelly.call("KVS.set", { key: "SmartHeatingSys" + _.sId, value: JSON.stringify(kvsS()) },
797 | function () {
798 | cntr--;
799 | });
800 | s.last = null;
801 | print(_.pId, "Script v", _.newV, (_.scId > 0 ? " created a schedule with ID:" + _.scId : "") + ", next heating calculation at", nxHr(1) + (_.updD < 10 ? ":0" : ":") + _.updD);
802 | wait(f_Wd);
803 | }
804 |
805 | //if the internet is not working or Elering is down
806 | function fMan() {
807 | if (_.manu) {
808 | return;
809 | }
810 | _.manu = true;
811 |
812 | let chpH = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 18, 19, 20];
813 | let eler = [];
814 | for (let i = 0; i < _.cPer; i++) { //create schedule for each period
815 | let hT = (i * c.tPer) + _.hTim; //find the end of the period
816 | hT = hT > 24 ? 24 : hT; //if the end of the period is more than 24, set it to 24
817 | for (let j = i * c.tPer; j < hT; j++) { //find the prices in each period
818 | eler.push([chpH[j], "-"]); //copy the price to the new array
819 | }
820 | }
821 | chpH = null;
822 | fTmr(); //set default timer
823 | fdSc(eler); //delete existing schedule and pass schedule data to create schedule
824 | }
825 |
826 | // Shelly doesnt support Javascript sort function so this basic math algorithm will do the sorting job
827 | function srAr(arr, sort) {
828 | let i, j, k, min, max, minX, maxX, tmp;
829 | j = arr.length - 1;
830 | for (i = 0; i < j; i++) {
831 | min = max = arr[i][sort];
832 | minX = maxX = i;
833 | for (k = i; k <= j; k++) {
834 | if (arr[k][sort] > max) {
835 | max = arr[k][sort];
836 | maxX = k;
837 | } else if (arr[k][sort] < min) {
838 | min = arr[k][sort];
839 | minX = k;
840 | }
841 | }
842 | tmp = arr[i];
843 | arr.splice(i, 1, arr[minX]);
844 | arr.splice(minX, 1, tmp);
845 |
846 | if (arr[minX][sort] === max) {
847 | tmp = arr[j];
848 | arr.splice(j, 1, arr[minX]);
849 | arr.splice(minX, 1, tmp);
850 | } else {
851 | tmp = arr[j];
852 | arr.splice(j, 1, arr[maxX]);
853 | arr.splice(maxX, 1, tmp);
854 | }
855 | j--;
856 | }
857 | return arr;
858 | }
859 |
860 | // Handle errors by logging and setting manual mode.
861 | function hErr(msg) {
862 | print(_.pId, "# Internet error, using historical cheap hours because ", msg);
863 | fMan(); //set schedule manually
864 | _.isLp = false;
865 | }
866 | // Wait for the RPC calls to be completed before starting next function.
867 | function wait(data) {
868 | if (cntr !== 0) {
869 | Timer.set(1000, false, wait, data);
870 | return;
871 | }
872 | if (typeof data === "function") { //if data is a function, call it
873 | data();
874 | } else { //if data is an array, the first element is a function and the second element is a parameter
875 | data[0](data[1]);
876 | }
877 | }
878 |
879 | // Next hour for heating calculation
880 | function nxHr(adHr) {
881 | const chkT = c.isFc && c.tPer > 0 ? c.tPer : 24;
882 | const hr = (Math.ceil((new Date(Date.now() + (adHr * 60 * 60 * 1000)).getHours() + 1) / chkT) * chkT) - 1;
883 | return hr > 23 ? 23 : hr;
884 | }
885 |
886 | /**
887 | Getting prices or forecast for today if
888 | * prices or forecast have never been fetched OR
889 | * prices or forecast are not from today or yesterday OR
890 | * prices or forecast needs regular update
891 | */
892 | function updt(ts) {
893 | const nHr = nxHr(0); //next hour for heating calculation
894 | const now = new Date(); //now
895 | const yDt = new Date(now - 60 * 60 * 24 * 1000); //yesterday
896 | const tDt = new Date(ts * 1000); //timestamp
897 | const tTd = tDt.getFullYear() === now.getFullYear() && tDt.getMonth() === now.getMonth() && tDt.getDate() === now.getDate();
898 | const tYd = tDt.getFullYear() === yDt.getFullYear() && tDt.getMonth() === yDt.getMonth() && tDt.getDate() === yDt.getDate();
899 | const tAft = tDt.getHours() === nHr && tTd;
900 | const isTm = now.getHours() === nHr && now.getMinutes() >= _.updD;
901 | return (isTm && !tAft) || !(tTd || tYd);
902 | }
903 |
904 | // This loop is to update the heating schedule
905 | function loop() {
906 | if (_.isLp) {
907 | return;
908 | }
909 | _.isLp = true;
910 | if (updt(_.tsPr) || c.isFc && updt(_.tsFc)) { //check if the prices or forecast needs to be updated
911 | strt(); //start the program
912 | } else {
913 | _.isLp = false;
914 | }
915 | }
916 |
917 | let isTm = false; //check if Shelly has time
918 | let t_hd; //timer handle
919 | let t_ct = 0; //time counter
920 | let lNot = true; //loop notification
921 | function fcTm() {
922 | const epch = Shelly.getComponentStatus("sys").unixtime;
923 | if (epch > 0) {
924 | //if time is OK, then stop the timer
925 | Timer.clear(t_hd);
926 | isTm = true;
927 | print(_.pId, "Shelly has time ", new Date(epch * 1000));
928 | //start the main loop with a random delay (0-5 sec) to avoid the same starting time for concurrent instances
929 | Timer.set(Math.floor(Math.random() * 5) * 1000, false, loop);
930 | } else {
931 | t_ct++;
932 | print(_.pId, "Shelly has no time", t_ct, "seconds. We wait for the time to be set.");
933 | if (t_ct > 30 && lNot) {
934 | loop(); //start the main loop if the time is not set in 30 seconds
935 | lNot = false;
936 | }
937 | return;
938 | }
939 | }
940 |
941 | /* --------- WATCHDOG START --------- */
942 | /** find watchdog script ID */
943 | function f_Wd() {
944 | Shelly.call('Script.List', null, function (res) {
945 | if (res) {
946 | let id = 0;
947 | const scr = res.scripts;
948 | res = null;
949 | for (let i = 0; i < scr.length; i++) {
950 | if (scr[i].name === "watchdog") {
951 | id = scr[i].id;
952 | break;
953 | }
954 | }
955 | /** Create a new script (id==0) or stop the existing script (id<>0) if watchdog found. */
956 | if (id === 0) {
957 | Shelly.call('Script.Create', { name: "watchdog" }, putC, { id: id }); //create a new watchdog
958 | } else {
959 | Shelly.call('Script.Stop', { id: id }, putC, { id: id }); //stop the existing watchdog
960 | }
961 | }
962 | });
963 | }
964 |
965 | // Add code to the watchdog
966 | function putC(res, err, msg, data) {
967 | if (err !== 0) {
968 | print(_.pId, "Watchdog script not created:", msg, ". Schedule will not be deleted if heating script is stopped or deleted.");
969 | } else {
970 | let code = 'let scId=0;function strt(){Shelly.call("KVS.Get",{key:"SmartHeatingSys"+scId},(function(e){e&&delS(JSON.parse(e.value))}))}function delS(e){Shelly.call("Schedule.Delete",{id:e.ExistingSchedule},(function(e,t,d,l){0!==t?print("Script #"+scId,"schedule ",l.id," deletion by watchdog failed."):print("Script #"+scId,"schedule ",l.id," deleted by watchdog.")}),{id:e.ExistingSchedule}),updK(e)}function updK(e){e.ExistingSchedule=0,Shelly.call("KVS.set",{key:"SmartHeatingSys"+scId,value:JSON.stringify(e)})}Shelly.addStatusHandler((function(e){"script"!==e.name||e.delta.running||(scId=e.id,strt())}));'
971 | const id = res.id > 0 ? res.id : data.id; //get the script ID
972 | Shelly.call('Script.PutCode', { id: id, code: code }, function (res, err, msg, data) {
973 | if (err === 0) {
974 | a_St(data.id);
975 | } else {
976 | print(_.pId, "Code is not added to the script:", msg, ". Schedule will notbe deleted if heating script is stopped or deleted.")
977 | }
978 | }, { id: id });
979 | }
980 | }
981 |
982 | // Enable autostart for the watchdog
983 | function a_St(sId) {
984 | if (!Shelly.getComponentConfig("script", sId).enable) {
985 | Shelly.call('Script.SetConfig', { id: sId, config: { enable: true } }, function (res, err, msg) {
986 | if (err !== 0) {
987 | print(_.pId, "Watchdog script autostart is not enabled.", msg, ". After Shelly restart, this script will not start and schedule is not deleted if heating script is stopped or deleted.");
988 | }
989 | });
990 | }
991 | // Start the watchdog
992 | Shelly.call('Script.Start', { id: sId }, function (res, err, msg) {
993 | if (err === 0) {
994 | print(_.pId, "Watchdog script created and started successfully.");
995 | print("// Memory Used:", Shelly.getComponentStatus("script", Shelly.getCurrentScriptId()).mem_used,
996 | "Peak:", Shelly.getComponentStatus("script", Shelly.getCurrentScriptId()).mem_peak);
997 |
998 | } else {
999 | print(_.pId, "Watchdog script is not started.", msg, ". Schedule will not be deleted if heating script is stopped or deleted.");
1000 | }
1001 | });
1002 | _.isLp = false;
1003 | }
1004 | /* --------- WATCHDOG END --------- */
1005 |
1006 | t_hd = Timer.set(1000, true, fcTm); //start the Shelly timecheck timer
1007 | Timer.set(_.freq * 1000, true, loop); //start the main loop with a frequency of 5 minutes
1008 |
--------------------------------------------------------------------------------