├── 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 | |Open Schedule|Open Schedule| 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 | Shelly KVS 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 | Shelly KVS 134 | 135 | Shelly KVS 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 | Elektrilevi Võrk 1 | 168 | | ``VORK2`` | **Elektrilevi**
Päeval 60 EUR/MWh
Öösel 35 EUR/MWh | Elektrilevi Võrk 2, 4 | 169 | | ``VORK4`` | **Elektrilevi**
Päeval 37 EUR/MWh
Öösel 21 EUR/MWh | Elektrilevi Võrk 2, 4 | 170 | | ``VORK5`` | **Elektrilevi**
Päeval 53 EUR/MWh
Öösel 30 EUR/MWh
Päeva tipp 82 EUR/MWh
Puhke tipp 47 EUR/MWh | Elektrilevi Võrk 5 Elektrilevi Võrk 5 | 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 | Insert code 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: "Failed
Check again in 5 minute" } 293 | D -- Succeeded
Calculate heating schedules --> L{Check, if market price and
forecast needs update} 294 | D --> M 295 | L --> M 296 | L -- Yes
Start 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 schedule
and 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 | | Thermia Villa | Thermia Eko Classic | 348 | 349 | Ühenda **kaks Shelly seadet** maasoojuspumba sees vastavalt **skeemile**. 350 | 351 | Connect Thermia and Shelly 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 | Küttegraafik 24-tunniseks perioodiks 410 | 411 | ____ 412 | 413 | * 12-tunnise perioodi küttegraafik ja kütteaja sõltuvus välistemperatuuri ja ``heatingCurve`` parameetrist. 414 | 415 | Küttegraafik 12-tunniseks perioodiks 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 | Kütteperiood 24 tundi 426 | 427 | ___ 428 | 429 | * 4-tunnine graafik ja kuidas 1 kõige odavam kütmise tund valitakse iga 4-tunnise perioodi kestel. 430 | 431 | Kütteperiood 4 tundi 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 | Couldn't get script. 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 | Key Value Storage 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 | |Open Schedule|Open Schedule| 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 | Shelly KVS 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 | Shelly KVS 141 | 142 | Shelly KVS 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| Elektrilevi Võrk 1 | 175 | |``VORK2``|Elektrilevi
Day 60 EUR/MWh
Night 35 EUR/MWh|Elektrilevi Võrk 2, 4| 176 | |``VORK4``|Elektrilevi
Day 37 EUR/MWh
Night 21 EUR/MWh|Elektrilevi Võrk 2, 4| 177 | |``VORK5``|Elektrilevi
Day 53 EUR/MWh
Night 30 EUR/MWh
Day Peak time 82 EUR/MWh
Holiday Peak Time 47 EUR/MWh|Elektrilevi Võrk 5Elektrilevi Võrk 5| 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 | Insert code 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 | Insert code 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: "Failed
Check again in 5 minute" } 304 | D -- Succeeded
Calculate heating schedules --> L{Check, if market price and
forecast needs update} 305 | D --> M 306 | L --> M 307 | L -- Yes
Start 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 schedule
and 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 | | Thermia Villa | Thermia Eko Classic | 362 | 363 | Connect **two Shelly devices** inside the heat pump following the **schema below**. 364 | 365 | Connect Thermia and Shelly 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 | Heating curve for 24h period 424 | 425 | ____ 426 | 427 | * 12 hour period graph represents visually how heating time varies with outside temperature and the ``heatingCurve`` parameter. 428 | 429 | Heating curve for 12h period 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 | Heating period 24h 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 | Heating period 24h 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 | Couldn't get script. 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 | Key Value Storage 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 | --------------------------------------------------------------------------------