diff --git a/CHANGELOG.md b/CHANGELOG.md index aa97fac..89f0bc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). # Suomeksi +## [2.6.0] - 23.10.2023 +- Lisätty uusi ominaisuus: päivän keskiarvon käyttö hintarajana + - Jos syöttää mihin tahansa seuraavista kentistä arvon `avg`, käytetään päivän keskiarvoa kiinteän arvon sijaan + - **käsiohjaus**: hintaraja + - **jakson halvimmat tunnit**: aina päällä -raja, maksimihinta + ## [2.5.1] - 21.10.2023 (2) - Bugikorjaus: Jos asetti pakko-ohjauksen x tunniksi painikkeella, se kyllä toimi, mutta tuli virheilmoitus @@ -57,6 +63,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Versio 2 julkaistu (tehty täysin uusiksi) # In English +## [2.6.0] - 23.10.2023 +- Added new feature: using day average price instead of static price limit (by setting value to `avg`) ## [2.5.1] - 21.10.2023 (2) - Bugfix: Setting override hour using button caused an error (however it worked) diff --git a/README.md b/README.md index 3a238f4..0f38530 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,9 @@ Shelly-laitteisiin selaimella ohjattava pörssisähkösovellus, joka venyttää laitteen skriptien rajoja. Pyörittää käyttöliittymää omalla web-serverillä ja tallentaa asetuksensa Shellyn muistiin. -Jos haluat ohjata relekytkintä sähkön hinnan mukaan, ilman ulkopuolisia palveluita, niin tämä voi olla hyödyllinen. Käyttää suoraan Viron kantaverkkoyhtiön [elering.ee](https://dashboard.elering.ee/api) -rajapintaa, eli välissä ei ole kolmannen osapuolen palveluita. Skripti ei vaadi rekisteröitymistä mihinkään vaan se toimii "suoraan paketista". +Jos haluat ohjata Shellyn relekytkintä sähkön hinnan mukaan, ilman johonkin palveluun rekisteröitymistä, niin tämä voi olla hyödyllinen. + +Käyttää suoraan Viron kantaverkkoyhtiön [elering.ee](https://dashboard.elering.ee/api) -rajapintaa, eli välissä ei ole muita palveluita. Skripti ei vaadi rekisteröitymistä mihinkään vaan se toimii "suoraan paketista". ![porssisahko](https://github.com/jisotalo/shelly-porssisahko/assets/13457157/751cbd0c-1b7a-4086-9e32-b04b888c5425) @@ -15,7 +17,7 @@ Jos haluat ohjata relekytkintä sähkön hinnan mukaan, ilman ulkopuolisia palve ## Ominaisuudet * Oma web-serveri Shellyn sisällä ja siinä pyörivä käyttöliittymä * Valvonta ja konfigurointi selaimen avulla -* Ei liityntöjä 3. osapuolen palveluihin +* Ei tarvitse rekisteröityä mihinkään * Kolme ohjaustapaa: * **käsiohjaus** - yksinkertaisesti ohjaus päälle/pois * **hintaraja** - jos hinta on alle rajan, laitetaan ohjaus päälle @@ -29,6 +31,7 @@ Jos haluat ohjata relekytkintä sähkön hinnan mukaan, ilman ulkopuolisia palve * Shelly Plus 1 * Shelly Pro 1 * Shelly Pro 2 + * Shelly Plus Plug S * *Laita viestiä jos sinulla on kokemusta muista laitteista!* ## Sisällysluettelo @@ -41,12 +44,12 @@ Jos haluat ohjata relekytkintä sähkön hinnan mukaan, ilman ulkopuolisia palve + [Ohjaustapa: Hintaraja](#ohjaustapa-hintaraja) + [Ohjaustapa: Jakson halvimmat tunnit](#ohjaustapa-jakson-halvimmat-tunnit) + [Toiminnot](#toiminnot) +- [Kysymyksiä ja vastauksia](#kysymyksiä-ja-vastauksia) - [Teknistä tietoa ja kehitysympäristö](#teknistä-tietoa-ja-kehitysympäristö) + [Lyhyesti](#lyhyesti) + [Tiedostot ja kansiot](#tiedostot-ja-kansiot) + [Muistin käyttö](#muistin-käyttö) + [Kehitysympäristö](#kehitysympäristö) -- [FAQ](#faq) - [In English](#in-english) - [License](#license) @@ -147,7 +150,7 @@ Hintarajaohjauksella lähtö asetetaan päälle jos sähkön hinta on alle mää | Asetus | Selite | Esim. (kuva yllä) | --- | --- | --- -| Hintaraja | Hinta, jossa ja jonka alla lähtö asetetaan päälle. [c/kWh] | `4.25` +| Hintaraja | Hinta, jossa ja jonka alla lähtö asetetaan päälle. [c/kWh]

Voit syöttää tähän myös arvon `avg`, jolloin käytetään päivän hinnan keskiarvoa. | `4.25` ### Ohjaustapa: Jakson halvimmat tunnit @@ -163,8 +166,8 @@ Versiosta 2.4.0 lähtien voidaan myös määrittää, että päälläolotuntien | Ajanjakso | Minkä mittaisiin jaksoihin vuorokausi jaetaan. Jokaiselta jaksolta haetaan sitten halvimmat tunnit. [h] | `6` | Tuntimäärä | Kuinka monta halvinta tuntia lähtö ohjataan päälle ajanjakson aikana.

Eli jos ajanjakso on 6h ja tuntimäärä 2, kello 00:00-06:00 lähtö ohjataan päälle kahtena halvimpana tuntina. Kuten myös kello 06:00-12:00 ja niin edelleen. | `2` | Peräkkäiset | Jos käytössä, valitaan jakson tunnit siten että ne ovat peräkkäin.

Näin yksittäisiä halvimpia tunteja ei välttämättä hyödynnetä, mutta halvin mahdollinen yhtenäinen jakso otetaan käyttöön. Katso esimerkki alta. | `ei` -| Aina päällä -raja | Jos sähkö on tätä halvempaa (tai juuri tämän hintaista) niin lähtö on aina päällä. [c/kWh] | `-0.5` -| Maksimihinta | Jos sähkön hinta on tätä korkeampi, lähtöä ei aseteta päälle vaikka tunti olisikin halvimpia tunteja. [c/kWh]

Tämän kanssa pitää olla tarkkana, jos tulee kalliita päiviä. | `30` +| Aina päällä -raja | Jos sähkö on tätä halvempaa (tai juuri tämän hintaista) niin lähtö on aina päällä. [c/kWh]

Voit syöttää tähän myös arvon `avg`, jolloin käytetään päivän hinnan keskiarvoa. | `-0.5` +| Maksimihinta | Jos sähkön hinta on tätä korkeampi, lähtöä ei aseteta päälle vaikka tunti olisikin halvimpia tunteja. [c/kWh]

Voit syöttää tähän myös arvon `avg`, jolloin käytetään päivän hinnan keskiarvoa.

*Tämän kanssa pitää olla tarkkana, jos tulee kalliita päiviä.* | `30` Alla esimerkki miten ohjaukset menenivät 12.10.2023 hinnoilla ja yllä olevilla asetuksilla (6h, 2 halvinta tuntia, aina päällä -raja -0.5 c/kWh). Huomaa jaksojen korostus taustavärillä. @@ -195,6 +198,23 @@ Valitaan kolme perättäistä tuntia. Valitaan kello 17-19 koska niiden hinnan k * **Shelly** * Avaa uudessa välilehdessä Shellyn oman hallintasivun +## Kysymyksiä ja vastauksia +### Miksi välillä tulee HTTP error 503? + +Tällä hetkellä jos skripti hakee hintoja tai suorittaa ohjauslogiikkaa, vastataan kaikkiin HTTP-pyyntöihin 503 (Service Unavailable). Käyttöliittymä osaa hallita tämän. + +Jos hintojen hakeminen ei onnistu, voi tämä virhe tulla käyttöliittymää avatessa (hintojen haun aikakatkaisu on 5s --> pahimmillaan virhe voi tulla 5 sekunnin ajan). Yritä avata sivu uudelleen. + +Voi olla että muutan tätä myöhemmin, vaatii vielä testejä. Syy on jälleen muistin säästäminen. + +### Miten ohjaan ainoastaan yön halvimmilla tunneilla? + +Aseta ohjaustavaksi `jakson halvimmat tunnit` ja päivän siirtohinnaksi `999` c/kWh. Näin kaikki päivätunnit ovat kalliita ja halvimmat tunnit valitaan sen johdosta yöajalta. + +### Miten saan lähdön päälle aina jos sähkön hinta on keskiarvon alapuolella? + +Versiosta 2.6.0 lähtien tämä onnistuu valitsemalla ohjaustavaksi `hintaraja` ja asettamalla hintarajaksi arvon `avg`. + ## Teknistä tietoa ja kehitysympäristö ### Lyhyesti @@ -226,7 +246,7 @@ Valitaan kolme perättäistä tuntia. Valitaan kello 17-19 koska niiden hinnan k ### Muistin käyttö -Versio 2.5.0 vie enimmillään 24276 tavua muistia (Shellyn maksimi 25200). Eli pieni skripti saattaa mahtua rinnalle pyörimään. +Versio 2.6.0 vie enimmillään 24440 tavua muistia (Shellyn maksimi 25200). Eli pieni skripti saattaa mahtua rinnalle pyörimään. ### Kehitysympäristö @@ -254,26 +274,13 @@ Käyttää Node.js -ympäristöä. * käyttöliittymän kehitystä varten * käynnistää paikallisen web-serverin ja tarjoaa `src/statics/` -kansion tiedostot portista 3000 -## FAQ -**Miksi välillä tulee HTTP error 503?** - -Tällä hetkellä jos skripti hakee hintoja tai suorittaa ohjauslogiikkaa, vastataan kaikkiin HTTP-pyyntöihin 503 (Service Unavailable). Käyttöliittymä osaa hallita tämän. - -Jos hintojen hakeminen ei onnistu, voi tämä virhe tulla käyttöliittymää avatessa (hintojen haun aikakatkaisu on 5s --> pahimmillaan virhe voi tulla 5 sekunnin ajan). Yritä avata sivu uudelleen. - -Voi olla että muutan tätä myöhemmin, vaatii vielä testejä. Syy on jälleen muistin säästäminen. - -**Voinko ohjata ainoastaan yön halvimmilla tunneilla?** - -Aseta ohjaustavaksi `jakson halvimmat tunnit` ja päivän siirtohinnaksi 999 c/kWh. Näin kaikki päivätunnit ovat kalliita ja halvimmat tunnit valitaan sen johdosta yöajalta. - ## In English -This is a script to control relay by Nordpool electric spot prices for Shelly products (especially Shelly Plus 1PM) with web-based user interface. +This is a script to control relay by Nordpool electric spot prices for Shelly products with web-based user interface. At the moment it's available only in Finnish and the spot price is queried for Finland. -There will soon be an English version (with country selection) available. +There will be an English version (maybe with country selection) available later when I have an insipiration to work on it. ## License diff --git a/dist/shelly-porssisahko.js b/dist/shelly-porssisahko.js index 3f6e4d0..3fbd50d 100644 --- a/dist/shelly-porssisahko.js +++ b/dist/shelly-porssisahko.js @@ -1 +1 @@ -let C_HIST=24,C_ERRC=3,C_ERRD=120,C_DEF={mode:0,m0:{cmd:0},m1:{lim:0},m2:{per:24,cnt:0,lim:-999,sq:0,m:999},vat:24,day:0,night:0,bk:0,err:0,out:0,fh:0,inv:0},c={s:{v:"2.5.1",st:0,cmd:0,chkTs:0,errCnt:0,errTs:0,upTs:0,timeOK:0,configOK:0,fCmdTs:0,p:{ts:0,now:0,low:0,high:0,avg:0}},p:[],h:[],c:C_DEF},l=!1,i=!1;function o(t,s){s-=t;return 0<=s&&s<3600}function a(t){return Math.floor((t?t.getTime():Date.now())/1e3)}function n(t,s,e){let n=t.toString();for(;n.length=C_ERRC&&a(t)-c.s.errTs=C_ERRC&&(c.s.errCnt=0);return s}()){let s=new Date;try{let t=s.getFullYear()+"-"+n(s.getMonth()+1,2,"0")+"-"+n(f(s),2,"0")+"T00:00:00%2b03:00";var e=t.replace("T00:00:00","T23:59:59");let l={url:"https://dashboard.elering.ee/api/nps/price/csv?fields=fi&start="+t+"&end="+e,timeout:5,ssl_ca:"*"};s=null,t=null,Shelly.call("HTTP.GET",l,function(s,t,e){l=null;try{if(0!==t||null==s||200!==s.code||!s.body_b64)throw new Error("conn.err ("+e+") "+JSON.stringify(s));{s.headers=null,e=s.message=null,c.p=[],c.s.p.high=-999,c.s.p.low=999,s.body_b64=atob(s.body_b64),s.body_b64=s.body_b64.substring(s.body_b64.indexOf("\n")+1);let t=0;for(;0<=t;){s.body_b64=s.body_b64.substring(t);var n=[t=0,0];if(0===(t=s.body_b64.indexOf('"',t)+1))break;n[0]=Number(s.body_b64.substring(t,s.body_b64.indexOf('"',t))),t=s.body_b64.indexOf('"',t)+2,t=s.body_b64.indexOf(';"',t)+2,n[1]=Number(s.body_b64.substring(t,s.body_b64.indexOf('"',t)).replace(",",".")),n[1]=n[1]/10*(100+(0c.s.p.high&&(c.s.p.high=n[1]),n[1]c.c.m2.m&&(i=!1,c.s.st=11)):c.s.timeOK?(c.s.st=7,t=1<=C_HIST;)c.h.splice(0,1);c.h.push([a(),i?1:0]),n&&n(!0)}else n&&n(!1)},e)}catch(t){b(t),l=!1}}let A=0,u=0,h=0;function y(){var s=a();for(let t=0;t=C_ERRC&&c(t)-a.s.errTs=C_ERRC&&(a.s.errCnt=0);return e}()){let e=new Date;try{let t=e.getFullYear()+"-"+n(e.getMonth()+1,2,"0")+"-"+n(f(e),2,"0")+"T00:00:00%2b03:00";var s=t.replace("T00:00:00","T23:59:59");let l={url:"https://dashboard.elering.ee/api/nps/price/csv?fields=fi&start="+t+"&end="+s,timeout:5,ssl_ca:"*"};e=null,t=null,Shelly.call("HTTP.GET",l,function(e,t,s){l=null;try{if(0!==t||null==e||200!==e.code||!e.body_b64)throw new Error("conn.err ("+s+") "+JSON.stringify(e));{e.headers=null,s=e.message=null,a.p=[],a.s.p.high=-999,a.s.p.low=999,e.body_b64=atob(e.body_b64),e.body_b64=e.body_b64.substring(e.body_b64.indexOf("\n")+1);let t=0;for(;0<=t;){e.body_b64=e.body_b64.substring(t);var n=[t=0,0];if(0===(t=e.body_b64.indexOf('"',t)+1))break;n[0]=Number(e.body_b64.substring(t,e.body_b64.indexOf('"',t))),t=e.body_b64.indexOf('"',t)+2,t=e.body_b64.indexOf(';"',t)+2,n[1]=Number(e.body_b64.substring(t,e.body_b64.indexOf('"',t)).replace(",",".")),n[1]=n[1]/10*(100+(0a.s.p.high&&(a.s.p.high=n[1]),n[1]("avg"==a.c.m2.m?a.s.p.avg:a.c.m2.m)&&(i=!1,a.s.st=11)):a.s.timeOK?(a.s.st=7,t=1<=C_HIST;)a.h.splice(0,1);a.h.push([c(),i?1:0]),n&&n(!0)}else n&&n(!1)},s)}catch(t){m(t),l=!1}}let g=0,h=0,p=0;function v(){var e=c();for(let t=0;t 0 && getDate(new Date(_.s.p.ts * 1000)) === getDate(now))) { //We have time and we have price data for today @@ -594,7 +594,7 @@ function logic() { } else if (_.c.mode === 1) { //Price limit - cmd = _.s.p.now <= _.c.m1.lim; + cmd = _.s.p.now <= (_.c.m1.lim == "avg" ? _.s.p.avg : _.c.m1.lim); //log("moodi on hintaraja (" + _.c.m1.priceLimit + "), ohjaus: " + (cmd ? "PÄÄLLE" : "POIS"), me); _.s.st = cmd ? 2 : 3; @@ -604,13 +604,13 @@ function logic() { _.s.st = cmd ? 5 : 4; //always on price limit - if (!cmd && _.s.p.now <= _.c.m2.lim) { + if (!cmd && _.s.p.now <= (_.c.m2.lim == "avg" ? _.s.p.avg : _.c.m2.lim)) { cmd = true; _.s.st = 6; } //maximum price - if (cmd && _.s.p.now > _.c.m2.m) { + if (cmd && _.s.p.now > (_.c.m2.m == "avg" ? _.s.p.avg : _.c.m2.m)) { cmd = false; _.s.st = 11; } diff --git a/src/statics/tab-config.js b/src/statics/tab-config.js index 99a03d2..afe8ced 100644 --- a/src/statics/tab-config.js +++ b/src/statics/tab-config.js @@ -59,6 +59,7 @@ try { let c = state.c let n = (v) => Number(v); + let avgn = (e) => qs(e).value == "avg" ? "avg" : n(qs(e).value); c.mode = n(qs("#mode").value); c.out = n(qs("#out").value); @@ -80,12 +81,12 @@ c.err = qs("#err").checked ? 1 : 0; c.m0.cmd = qs("#m0-cmd").checked ? 1 : 0; - c.m1.lim = n(qs("#m1-lim").value); + c.m1.lim = avgn("#m1-lim"); c.m2.per = n(qs("#m2-per").value); c.m2.cnt = Math.min(c.m2.per, n(qs("#m2-cnt").value)); c.m2.sq = qs("#m2-sq").checked ? 1 : 0; - c.m2.lim = n(qs("#m2-lim").value); - c.m2.m = n(qs("#m2-m").value); + c.m2.lim = avgn("#m2-lim"); + c.m2.m = avgn("#m2-m"); DBG(me(), "Settings to save:", c); diff --git a/src/statics/tab-status.js b/src/statics/tab-status.js index 8aacaf5..6fcee9b 100644 --- a/src/statics/tab-status.js +++ b/src/statics/tab-status.js @@ -103,9 +103,9 @@ let date = new Date(row[0] * 1000); let cmd = (c.mode === 0 && c.m0.cmd) - || (c.mode === 1 && row[1] <= c.m1.lim) - || (c.mode === 2 && cheapest.includes(i) && row[1] <= c.m2.m) - || (c.mode === 2 && row[1] <= c.m2.lim) + || (c.mode === 1 && row[1] <= (c.m1.lim == "avg" ? s.p.avg : c.m1.lim)) + || (c.mode === 2 && cheapest.includes(i) && row[1] <= (c.m2.m == "avg" ? s.p.avg : c.m2.m)) + || (c.mode === 2 && row[1] <= (c.m2.lim == "avg" ? s.p.avg : c.m2.lim)) || (c.fh & (1 << i)) == (1 << i); //Invert