Spot-hinta.fi - Yksinkertainen ohjelmistorajapinta (API) sähkön pörssihinnan hakemiseen

tet

Hyperaktiivi
Nyt olisi Mikille korjattavaa? API antaa verollisen hintatiedon verollisena myös negatiivisen hinnan tunneilta, vaikka ilmeisesti ei pitäisi. Ainakin jos katsoo vaikka sahko.tk tai porssisahko.net -sivustojen hintatietoja, ja vertaa niitä NordPoolin verottomiin hintoihin. Niissä negatiivisten tuntien luvut ovat samoja kuin NordPoolissa, positiivisissa on ALV vielä päälle. Sen sijaan api.spot-hinta.fi antaa myös negatiiviset verollisena, ainakin tuo "TodayAndDayForward" endpoint.
 

Mikki

Hyperaktiivi
Jaa... Mitenkähän tuo oikeasti menee. Kieltämättä voi tuo olla väärinkin.

Jos korjaus on että negatiivisen summan ALV on 0% niin korjaus on helppo.

EDIT: korjaus on nyt tehty.
 
Viimeksi muokattu:
  • Tykkää
Reactions: tet

ilmarix

Tulokas
Jaa... Mitenkähän tuo oikeasti menee. Kieltämättä voi tuo olla väärinkin.

Jos korjaus on että negatiivisen summan ALV on 0% niin korjaus on helppo.

EDIT: korjaus on nyt tehty.

Twitterissä verouutiset sanoi viime vuonna näin:


Eli jos tuntitasolla laskee niin -1.00 ALV0 on -1.24 ALV24.

Aika moni käyttänyt sahko.tk lähteenä ja turhaan "korjattu" toimivia sivustoja...
 

janti

Moderaattori
Ylläpidon jäsen
Eli jos tuntitasolla laskee niin -1.00 ALV0 on -1.24 ALV24.

Aika moni käyttänyt sahko.tk lähteenä ja turhaan "korjattu" toimivia sivustoja...
Tuosta vastauksesta saa sen käsityksen että ensin lasketaan kaikki ALV 0% ja sitten lisätään lopuksi se ALV 24%.
Tuossa ei mielestäni oteta kantaa siihen että koko kuukauden ALV0% lasku olisi negaiivinen.
 

Mikki

Hyperaktiivi
Eli tavallaan yksittäisen tunnin ALV on mahdoton laskea insinööripornon tarkkuudella, jos kuukaudessa on ollut yksikin miinustunti. Siis etukäteen... Vasta kun kuukauden kaikki tunnit on tiedossa.

Nyt menee yöunet monelta. :)
 

janti

Moderaattori
Ylläpidon jäsen
Eli tavallaan yksittäisen tunnin ALV on mahdoton laskea insinööripornon tarkkuudella, jos kuukaudessa on ollut yksikin miinustunti. Siis etukäteen... Vasta kun kuukauden kaikki tunnit on tiedossa.
Itse summaan omassa exelissä nyt siihen yhteen pörssisähkön miinustuntiin sen katteen ALV0%:lla ja sitten vasta lasken summasta ALV24%. Tällä hetkellä en ole huomioinut tilannetta missä tuokin on/olisi negatiivinen. :)
 

mobbe

Vakionaama
Alv lasketetaan tietysti vasta verottomasta määrästä laskulle eli vaikka kuukauden veroton ostosähkön määrä on 100 euroa tälle vasta lisätään 24% laskussa 124e.Laskuun on kerääntynyt myös negatiivisia spot-kilowattitunteja mutta laskuja ei lähetetä tunnin välein.
 
Viimeksi muokattu:

janti

Moderaattori
Ylläpidon jäsen
Aurinkosähköntuottajilla on todennäköisempää se että voisi joutua maksamaan tuon ALV:n ylituotannosta joltain kuulta aurinkosähkön ostajalle. Tuosta voi jatkaa tässä ketjussa.
 

Kamis

Aktiivinen jäsen
Mä järkeilin ostajan tilanteen näin:

Asiakas ostaa 100kWh hintaan 0,02€/kWh (ALV 0%) ja 20 kWh hintaan -0,01€/kWh (ALV 0).
=100*0,02 + (-0,01*20) = 1,8€ (ALV 0%)
Ja siihen ALV
= 1,8 *1,24 = 2,232€(ALV24%)

Sama loppusumman sain vaikka energian hinta/kWh:ssa, myös negatiivisessa, olisi jo valmiiksi ALV mukana
0,02€/kWh *1,24 = 0,0248€/kWh (ALV24)
-0,01€/kWh *1,24 = -0,0124€/kWh (ALV24)

100*0,0248 + (-0,0124*20) = 2,232€
 
Viimeksi muokattu:

mobbe

Vakionaama
1.Yksityishenkilö ei ole arvonlisäveron velvollinen tuottamastaan aurinkosähkön ylijäämästä.
2.Yksityishenkilö ei ole arvonlisäveron velvollinen tuottamastaan aurinkosähkön negatiivisestä ylijäämästä
3.Laskutuksessa lasketaan yhteen kuukauden ylijäämä joka tunnista yhteen joka hyvitetään laskussa verottomasti.
4.Positiiviset tuntihinnat lisäävät ja negatiiviset tuntihinnat vähentävät hyvitettävän summan määrää laskulla.
5.Tuotannon marginaali lasketaan erikseen mutta ei merkitystä sillä jos ylijäämää tuotettu myös negatiivisella hinnalla marginaali laskutetaan
 

tet

Hyperaktiivi
Mä järkeilin ostajan tilanteen näin:

Asiakas ostaa 100kWh hintaan 0,02€/kWh (ALV 0%) ja 20 €/kWh hintaan -0,01€/kWh (ALV 0).
=100*0,02 + (-0,01*20) = 1,8€ (ALV 0%)
Ja siihen ALV
= 1,8 *1,24 = 2,232€(ALV24%)

Sama loppusumman sain vaikka energian hinta/kWh:ssa, myös negatiivisessa, olisi jo valmiiksi ALV mukana
0,02€/kWh *1,24 = 0,0248€/kWh (ALV24)
-0,01€/kWh *1,24 = -0,0124€/kWh (ALV24)

100*0,0248 + (-0,0124*20) = 2,232€

Totta mooses, noinhan se menee. Laskin nuo esimerkkisi myös marginaalin kanssa, ja lopputulema oli silti molemmissa sama. Eli sittenkin se ALV pitää olla mukana myös negatiivisissa hinnoissa, silloin summat täsmäävät. Höh, menin harhaan kun kahdessakin eri nettisivustossa oli jätetty ALV pois negatiivista summista. Väärinhan ne silloin näyttävät.
 

janti

Moderaattori
Ylläpidon jäsen
Totta mooses, noinhan se menee. Laskin nuo esimerkkisi myös marginaalin kanssa, ja lopputulema oli silti molemmissa sama.
Niin miten se meenee jos kulutukset olisivatkin tuossa esimerkissä toisinpäin? :)
=> Tuleeko sähkömyyjältä siis myös ALV takaisin?
 

tet

Hyperaktiivi
Niin miten se meenee jos kulutukset olisivatkin tuossa esimerkissä toisinpäin? :)
=> Tuleeko sähkömyyjältä siis myös ALV takaisin?

No tuota en kyllä laskenut, kun itselläni ei aurinkopaneeleita ole. Jos homma ei myyntiin menevän kanssa toimi, niin sittenhän "panelisteille" tarvitaan omat asetukset rajapintoihin. Peruskuluttajille rajapinta toimii oikein, jos myös negatiivisissa on ALV mukana.
 

janti

Moderaattori
Ylläpidon jäsen
Peruskuluttajille rajapinta toimii oikein, jos myös negatiivisissa on ALV mukana.
Itse lasken nyt yksittäisen tunnin hinnan niin että pörssisähkön hinta ALV0% johon lisään katteen ALV0% , tämän jälkeen lisään ALV24%. Väreellä tämä kaava ei nyt toimi, koska veloittavat kuitenkin tuon katteen kaikissa tilanteissa.
=> Eli mulla on negatiivinen ALV mukana omassa Excelöinnissä. :cool:
 

tet

Hyperaktiivi
Itse lasken nyt yksittäisen tunnin hinnan niin että pörssisähkön hinta ALV0% johon lisään katteen ALV0% , tämän jälkeen lisään ALV24%. Väreellä tämä kaava ei nyt toimi, koska veloittavat kuitenkin tuon katteen kaikissa tilanteissa.
=> Eli mulla on negatiivinen ALV mukana omassa Excelöinnissä. :cool:

Siis onko sinulla myyntiä, vai puhutko nyt ihan vaan ostosähköstä ja sen negatiivisista hinnoista? Jos jälkimmäinen, niin juurihan Kamis tuossa yllä esimerkillä näytti, miten molemmilla tavoilla laskien saa saman tuloksen. Ja katteen lisäämällä homma toimii edelleen. Eli voit laskea veroton spot-hinta + veroton kate, ja lisätä saatuun tulokseen alvin. Tai sitten lasket vain verollinen spot-hinta + verollinen kate, ja tulos on sama, vaikka verollinen spot-hinta olisi negatiivinen. Katehan ei tietenkään muutu, se on aina sama, oli spot-hinta mitä vain. Mutta jos lasket hinnan verottomana, niin tietenkin myös katteen verottomana - ja päädyt samaan lopputulokseen kuin verollisilla hinnoilla.
 

janti

Moderaattori
Ylläpidon jäsen
Siis onko sinulla myyntiä, vai puhutko nyt ihan vaan ostosähköstä ja sen negatiivisista hinnoista?
Ihan teoreettista pohdintaa, josko joku onnistuisi ostamaan sähkönsä negatiiviseen hintaan jonkin kuun aikana. :cool:
Lataa vaikka lämpöä sinne porareikään tms. varastoon talvea varten. ;D

 
Viimeksi muokattu:

tet

Hyperaktiivi
Ihan teoreettista pohdintaa, josko joku onnistuisi ostamaan sähkönsä negatiiviseen hintaan jonkin kuun aikana. :cool:
Lataa vaikka lämpöä sinne porareikään tms. varastoon talvea varten. ;D

Jaa, meinasit että marginaalin kanssakin kuukauden loppusumma olisi negatiivinen? Lienee aika teoreettinen tilanne, mutta eiköhän silloin sähköyhtiö maksaisi vain loppusumman verottomana. Ehkä tämmöisissä rajapinnoissa ei tällaisia tapauksia tarvitse yrittääkään ottaa huomioon. :p

Mutta joo, se aiemmin ehdottamani ALVin poisto negatiivisista hinnoista oli huono idea. Mikki varmaan korjaa, jos ei jo ole korjannutkin?
 

kayttajatunnus

Aktiivinen jäsen
Mites tämän skriptin avulla voisi rakentaa MLP veden lämmityksen halvimmille tunneille siten, että kuitenkin lauantaisin tiettynä kellonaikana on legionellakuumennus aktiivinen, joka vaatii neljä tuntia yhtämittaista lämmitystä. https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Voiko tuosta ottaa samalle releelle kaksi eri settingsiä käyttöön siten, että toisessa on sallittuna kaikki muut päivät paitsi lauantai ja toisessa pelkästään lauantai?
 

Mikki

Hyperaktiivi
Mites tämän skriptin avulla voisi rakentaa MLP veden lämmityksen halvimmille tunneille siten, että kuitenkin lauantaisin tiettynä kellonaikana on legionellakuumennus aktiivinen, joka vaatii neljä tuntia yhtämittaista lämmitystä. https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Voiko tuosta ottaa samalle releelle kaksi eri settingsiä käyttöön siten, että toisessa on sallittuna kaikki muut päivät paitsi lauantai ja toisessa pelkästään lauantai?
Joo voi... oikeastaan juuri tämmöisiin tapauksiin on toiminto suunniteltu.
 

salosa

Jäsen
Tämä on hieno rajapinta ja saan tämän nätisti näkymään URLilla tai Postmanilla. Yritin ottaa sitä myös home-assistantin kanssa käyttöön tällä simppelillä ohjeella https://spot-hinta.fi/home-assistant/ . Onko home assistantin rest syntaksiin tullut muutoksia uusissa versioissa vai miksi minä en saa sitä toimimaan? En lukenut kaikkia 24 sivua läpi, jos tästä on jo aiemmin keskusteltu.
 
Viimeksi muokattu:

Mikki

Hyperaktiivi
Tämä on hieno rajapinta ja saan tämän nätisti näkymään URLilla tai Postmanilla. Yritin ottaa sitä myös home-assistantin kanssa käyttöön tällä simppelillä ohjeella https://spot-hinta.fi/home-assistant/ . Onko home assistantin rest syntaksiin tullut muutoksia uusissa versioissa vai miksi minä en saa sitä toimimaan? En lukenut kaikkia 24 sivua läpi, jos tästä on jo aiemmin keskusteltu.
Hyvä kun muistutit... tuokin skripti taisi mennä rikki HA:n uusimmassa päivityksessä. Mutta tuohon olikin julkaisematon korjaus... ota testiin, päivitin sen tuonne sivulle nyt (ja laitoin YV:nä).
 

tet

Hyperaktiivi
Pari toivetta:

1. Viimeinenkin tunti näkymään tuolla TodayAndDayForward-endpointissa. Nythän se leikkaa viimeisen tunnin, joka on meillä vuorokauden vaihteen takana, pois näkyvistä. Vai pitäisikö yhteensopivuuden takia olla uusi, pelkkä "TodayAndForward"? Kun kaupat käydään CST-aikavyöhykkeellä, tulee meille se yksi tunti ylihuomiselle, joka nyt tulee apista vasta seuraavana päivänä, vaikka varmaankin on kannassa?

2. Lisää desimaaleja. Alkaa tulla jo pyöristysvirheitä näillä olemattomilla hinnoilla, euromääräisiin hintoihin tulee turhan paljon nollia eteen.
 

My.Ai.Hu

Vakionaama
tuossa oma järjestelmää kun vunteeraa niin esim
kärkitieton aurinkoinvertteriltä että nyt saa lämmittää
kun on omaa tuotantoa.
Se ei varmastikkaan olisi kovin hankalaa.
tai sitten on

Mutta kiitokset hyvästä apista.
Täytyy käydä kahvilla joku päivä
 

korsteeni

Vakionaama
kui tuosta jq lla tai jollain muulla saisi 06-02-02:00 elikkä %m-%d %H
"DateTime": "2023-06-02T02:00:00+03:00"
D3js time format ei taivusitte millään vaikka parsii kuinka......pikemminkin en osaa
toimii mutta on niin pitkä plitania, saisi lyhyemmäksi
 

VesA

Moderaattori
Ylläpidon jäsen
kui tuosta jq lla tai jollain muulla saisi 06-02-02:00 elikkä %m-%d %H
"DateTime": "2023-06-02T02:00:00+03:00"
D3js time format ei taivusitte millään vaikka parsii kuinka......pikemminkin en osaa
toimii mutta on niin pitkä plitania, saisi lyhyemmäksi
Eikai sitä tarvitse varsinaisesti parsia. jq:lla sieltä saa tuon standardi-päivämäärän ulos, ja sitten vaikka cut + tr

echo "2023-06-02T02:00:00+03:00" | cut -c6-10,11-16 | tr T \-
06-02-02:00
 

korsteeni

Vakionaama
sehän tässä ihmetyttää, standardi-päivämäärä pitäisi olla ja on
mutta d3js ei kykene %H poimimaan siitä graafiinsa
jos tuo paikallinen olisi Z niin toimisi mutta +03:00 sekoittaa sen pään, influxissa joskus säädin saman tiimoilta
kätevintä tuo olisi hakuvaiheessa
 

tk-

Aktiivinen jäsen
sehän tässä ihmetyttää, standardi-päivämäärä pitäisi olla ja on
mutta d3js ei kykene %H poimimaan siitä graafiinsa
jos tuo paikallinen olisi Z niin toimisi mutta +03:00 sekoittaa sen pään, influxissa joskus säädin saman tiimoilta
kätevintä tuo olisi hakuvaiheessa
Harrastelijakoodarin epäily on, että jos sen vaan lukee jsonista, niin se ei ole päivämääräobjektina vaan string-muodossa. Eli ensin pitäisi muuttaa Date-objektiksi esimerkiksi new Date() -funktiolla. Useat graafit taitaa ymmärtää tuota date-objektia semmoisenaan, mutta sitä voi sitten myös formatoida haluamaansa muotoon tarvittaessa.

Itsellä on tuossa Pörssärin Home Assistant -sovellutuksen Apexchartin data-generatorissa juuri tuollainen tilanne, että jsonin key täytyy muuttaa date-objektiksi ja niistä tehdään sitten pari kyseisen tunnin hinnan kanssa data-arrayta varten. Githubissa on koodi tuosta graafista siellä lovelace-kansiossa.
 
Viimeksi muokattu:

korsteeni

Vakionaama
johtunee D3js versiosta, homeassistantissa toimii hyvin

D3js v3 ei ilmeisestikään tunnista tuota tämänpäivän standardimuotoa +03:00 kun on jo ikää
  • %Z - time zone offset, such as "-0700".
 

Jimi

Jäsen
Miksiköhän tuo scripti on tänään räpsytellyt shellyä edestakaisin ? Taitaakin olla jo aikas vanha versio pääsulakkeineen. Voisko tuonne kirjastoon saada myös version jolla ohjata sulkuaikakosketinta hyödyntäen kalleimmat yön tunnit pois. Nyt näyttää kaikki olettavan että kytketään päälle varaajaa ja lämmitystä, meillä sähkölämmityssysteemi toimii toisinpäin eli hoitaa lämmityksen kun ei ole sulkuaikaohjaus päällä.
1688408654386.png
 
Viimeksi muokattu:

Mikki

Hyperaktiivi
Miksiköhän tuo scripti on tänään räpsytellyt shellyä edestakaisin ? Taitaakin olla jo aikas vanha versio pääsulakkeineen. Voisko tuonne kirjastoon saada myös version jolla ohjata sulkuaikakosketinta hyödyntäen kalleimmat yön tunnit pois. Nyt näyttää kaikki olettavan että kytketään päälle varaajaa ja lämmitystä, meillä sähkölämmityssysteemi toimii toisinpäin eli hoitaa lämmityksen kun ei ole sulkuaikaohjaus päällä.

Tämä skripti: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Jos laitat tuon "Inverted" --> true, niin se tarkoittaa että "rank" määrän mukaisena tunteina on "rele auki" ja muina tunteina "kiinni". Eli toimii sitten päinvastoin kuin normaalisti.

Käyttämällä vaikka arvoa 16, niin sitten kahdeksana kalleimpana tuntina on rele vedettynä päälle ja muina tunteina rele on auki. Eli periaatteessa jos ymmärsin oikein, niin sen mitä haluat.


1688412659572.png
 

Jimi

Jäsen
Lisäksi oikeastaan tarve olisi huomioida myös päivä ja yö ajat koska systeemi vaihtaa klo 22 varaavaan lattialämmitykseen ja aamulla klo 7 suoraan kattolämmitykseen. Tarvittaisiin siis kahden jakson kalleimmat tunnit sulkuaikaan.
 

Jimi

Jäsen
Tämä skripti: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Jos laitat tuon "Inverted" --> true, niin se tarkoittaa että "rank" määrän mukaisena tunteina on "rele auki" ja muina tunteina "kiinni". Eli toimii sitten päinvastoin kuin normaalisti.

Käyttämällä vaikka arvoa 16, niin sitten kahdeksana kalleimpana tuntina on rele vedettynä päälle ja muina tunteina rele on auki. Eli periaatteessa jos ymmärsin oikein, niin sen mitä haluat.


katso liitettä 87402
Täytyypä kokeilla päivittää nuo scriptit, uudelleenkäynnistyksen jälkeen näyttää nuo vanhatkin pelaavan kuten pitää ilman räpsyjä.
 

Sammypiru

Vakionaama
Tälläkin yksi Shelly alkoi vetämään klo 21:01 ja lopetti klo 21:02, olisiko tämä räpsymistä.

Tai ainakin shellyn käynnistymisestä aktivoituva webhook IFTTT:n suuntaan aktivoitui ja minuutin päästä vuorostaan sammumisesta aktivoituva webhook aktivoitui.
 

Jimi

Jäsen
Tämä skripti: https://github.com/Spot-hinta-fi/Shelly/blob/main/Scripts/Shelly-Rank_and_Price_limit.js

Jos laitat tuon "Inverted" --> true, niin se tarkoittaa että "rank" määrän mukaisena tunteina on "rele auki" ja muina tunteina "kiinni". Eli toimii sitten päinvastoin kuin normaalisti.

Käyttämällä vaikka arvoa 16, niin sitten kahdeksana kalleimpana tuntina on rele vedettynä päälle ja muina tunteina rele on auki. Eli periaatteessa jos ymmärsin oikein, niin sen mitä haluat.


katso liitettä 87402
Tuota parametrien käyttöä voisi hiukan ohjeistaa lisää, ei täysin auennut. settingsejä on tuplasti, ilmeisesti toinen vain laitetaan false?, pitääkö käyttää sekä price limit että rank and price limit settingsejä vain vain yhtä ?, mitä eroa on price allowed ja max price ja miksi 0 ja 999?, mitä tapahtuu jos molemmissa settingseissä relayisinuse on true. Ohjeistus ja dokumentointi on nyt suurin este näiden scriptien käytölle ja muokkaamiselle omaan tarpeeseen.
 

Jimi

Jäsen
Mulle paras scripti ois sellainen että 22-07 välillä rele vetäis muina paitsi x halvimpana tuntina ja 07-22 välillä rele vetäis jos x hinta ylittyy. Eli yöllä varataan lattioita halvimpina tunteina ja päivällä lämmitetään jos se ei ole kallista. Rele ohjaa siis sähkölämmityksen sulkuaikatuloa estäen kalliin sähkön käytön.
 

kayttajatunnus

Aktiivinen jäsen
Mitenkäs tämä skripti nyt pitäisi rakentaa, että saan eri päiville eri asetukset? Eli lauantaisin klo 01-06 olisi tarkoitus tehdä legionellakuumennus. Tällä tavalla se ajaa molemmat asetukset peräkkäin ja jälkimmäinen, eli tuo lauantaille asetettu, jää voimaan.

Koodi:
// More information about the API (in Finnish): https://spot-hinta.fi/
// Support Shelly script and Spot-hinta.fi API development and maintenance: https://www.buymeacoffee.com/spothintafi

// This script can control up to four different relays:
// - Max two relays with PRICE limit only
// - Max two relays with RANK (number of cheapest hours) AND PRICE limit

// Region to use. See supported regions in Swagger documentation: https://api.spot-hinta.fi/swagger/ui
let Region = "FI";

// Settings - Price limit - rule 1
let SETTINGS_PRICELIMIT_1 =
{
    // User settings
    RelayIsInUse: false, // True/false: If you want to use this relay or not
    PriceAllowed: "30", // Price limit (in euro cents, without decimals). Use "-99" if not wanted. If price is now less than this the relay is turned ON (or OFF if inverted - see below)
    AllowedDays: "1,2,3,4,5,6,7", // Execution days: 1=Monday to 7=Sunday, separated with comma.
    Relay: "0",  // Shelly's relay number (Value is between 0-3 depending how many relays Shelly has). Make sure this is correct!
    RelayName: "OilBoiler",  // Name this relay. Name is used in debug log mostly.
    Inverted: false, // True/false: Set to "true" to inverted the relay logic

    // Script technical fields. Do not edit!
    Url: 1,
    SettingsNumber: 1,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Price limit - rule 2
let SETTINGS_PRICELIMIT_2 =
{
    // User settings
    RelayIsInUse: false,
    PriceAllowed: "20",
    AllowedDays: "1,2,3,4,5,6,7",
    Relay: "0",
    RelayName: "Charger",
    Inverted: false,

    // Script technical fields. Do not edit!
    Url: 1,
    SettingsNumber: 2,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Rank and price limit - rule 1
let SETTINGS_RANK_PRICE_1 =
{
    // User settings
    RelayIsInUse: true,
    Rank: "4", // "Rank" limit (number of cheapest hours today)
    PriceAllowed: "2",
    MaxPrice: "999", // Maximum allowed price in euro cents.
    AllowedDays: "1,2,3,4,5,7",
    BackupHours: ["00", "01", "02", "03", "04", "05"], // Backup hours; if API is not answering or internet connection is down.
    BoosterHours: "99,99", // Relay is always ON during booster hours. Use "99,99" to disable this.
    PriorityHours: "99,99", // Hours you want to prioritize. If PriceModifier: is "0" these hours always get the smallest 'rank'. Use "99,99" to disable this.
    PriorityHoursRank: "3",  // How many priority hours are prioritized. i.e. "3" = 3 cheapest priority hours.
    PriceModifier: "0", // If priority hours have lower price - such as 'night electricity' - the difference in Euro cents.
    Relay: "0",
    RelayName: "WaterHeater_normaali",
    Inverted: false,

    // Script technical fields. Do not edit!
    Url: 2,
    SettingsNumber: 3,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// Settings - Rank and price limit - rule 2
let SETTINGS_RANK_PRICE_2 =
{
    // User settings
    RelayIsInUse: true,
    Rank: "5",
    PriceAllowed: "2",
    MaxPrice: "999",
    AllowedDays: "6",
    BackupHours: ["01", "02", "03", "04", "05"],
    BoosterHours: "99,99",
    PriorityHours: "01,02,03,04,05",
    PriorityHoursRank: "5",
    PriceModifier: "0",
    Relay: "0",
    RelayName: "WaterHeater_legionella",
    Inverted: false,

    // Script technical fields. Do not edit!
    Url: 2,
    SettingsNumber: 4,
    RelayStatus: true,
    RelayStatusSource: "",
    RelayExecuted: false,
};

// ------------------------------------
// Script - no need to modify (usually)
// ------------------------------------

// Variables needed to control the execution
let currentHour = "";
let currentHourUpdated = "";
let rounds = 0;

// This is triggered by the timer (see end of the script)
function ExecuteRelayRules() {

    // Counter of exeutution rounds. First round means mostly.
    rounds = rounds + 1;

    // Update current hour in a global variable.
    UpdateCurrentHour(rounds);

    // Reset relays if hour has changed
    InitializeRelaysIfHourHasChanged(rounds);

    // This was initialization round. Next round is the first actual processing round.
    if (rounds === 1) { print("Script initialization done."); return; }

    // Return if current hour is already done.
    if (HasCurrentHourBeenDone() === true) {
        return;
    }

    // Execute Relays which are in use and not executed yet.
    ExecuteRelayRule(SETTINGS_PRICELIMIT_1);
    ExecuteRelayRule(SETTINGS_PRICELIMIT_2);
    ExecuteRelayRule(SETTINGS_RANK_PRICE_1);
    ExecuteRelayRule(SETTINGS_RANK_PRICE_2);
}

// Executes HTTP GET query for a given settings
function ExecuteRelayRule(Settings) {

    // Do nothing if relay is not in use or it is already executed for this hour
    if (Settings.RelayIsInUse === false || Settings.RelayExecuted === true) { return; }

    // Get URL
    let urlToCall = BuildUrl(Settings);

    // HTTP Get Request to API
    print("Execute HTTP GET. Relay name: " + Settings.RelayName + " - URL: " + urlToCall);
    Shelly.call("HTTP.Request", { method: "GET", url: urlToCall, timeout: 10, ssl_ca: "*" }, ProcessHttpRequestResponse, Settings);
}

// Process response for HTTP GET
function ProcessHttpRequestResponse(response, error_code, error_msg, Settings) {

    // Process response and get status if processing was done successfully.
    let response = SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings);

    // Update relay executed status
    if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayExecuted = response; }
    if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayExecuted = response; }
    if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayExecuted = response; }
    if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayExecuted = response; }
}

// Control the relays based on the result from the API call
function SetRelayStatusInShellyBasedOnHttpStatus(response, error_code, error_msg, Settings) {

    // Check for network errors
    if (error_code !== 0 || response === null) {
        print("Network error occurred: " + error_msg);
        RunBackupHourRule(Settings);
        return false;
    }

    if (response.code === 200) {
        SetRelayStatusInShelly(Settings, true, "api"); // Relay is turned on.
        return true;
    }
    else if (response.code === 400) {
        SetRelayStatusInShelly(Settings, false, "api"); // Relay is turned off.
        return true;
    }
    else {
        print("HTTP status code does not indicate success. HTTP status code: " + JSON.stringify(response.code));
        RunBackupHourRule(Settings);
        return false;
    }
}

// Change relay status in Shelly
function SetRelayStatusInShelly(Settings, newStatus, relayStatusSource) {

    // Set status messages
    let statusMessage = "";
    if (newStatus === true && Settings.Inverted === true) { statusMessage = "Turn relay off (inverted). Relay name: " + Settings.RelayName; }
    if (newStatus === false && Settings.Inverted === true) { statusMessage = "Turn relay on (inverted). Relay name: " + Settings.RelayName; }
    if (newStatus === true && Settings.Inverted === false) { statusMessage = "Turn relay on. Relay name: " + Settings.RelayName; }
    if (newStatus === false && Settings.Inverted === false) { statusMessage = "Turn relay off. Relay name: " + Settings.RelayName; }

    // Switch behavior based on Inverted-setting
    if (Settings.Inverted === true && newStatus === true) { newStatus = false; }
    else if (Settings.Inverted === true && newStatus === false) { newStatus = true; }

    // Don't close relay if it is already registered as closed AND source in last control is 'api'
    // When closing has been done by 'backupHour', relay can be closed again.
    if (Settings.RelayStatus === false && newStatus === false && Settings.RelayStatusSource === "api") {
        print("Relay is already closed. Not closing again (this allows possible other script to act)");
        return;
    }

    // Set relay in Shelly
    print(statusMessage);
    Shelly.call("Switch.Set", "{ id:" + Settings.Relay + ", on:" + JSON.stringify(newStatus) + "}", null, null);

    // Save status information into settings
    SetRelayStatusInSettings(Settings, newStatus, relayStatusSource);
}

// Set relay status for a given settings
function SetRelayStatusInSettings(Settings, newStatus, relayStatusSource) {

    if (Settings.SettingsNumber === 1) { SETTINGS_PRICELIMIT_1.RelayStatus = newStatus; SETTINGS_PRICELIMIT_1.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 2) { SETTINGS_PRICELIMIT_2.RelayStatus = newStatus; SETTINGS_PRICELIMIT_2.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 3) { SETTINGS_RANK_PRICE_1.RelayStatus = newStatus; SETTINGS_RANK_PRICE_1.RelayStatusSource = relayStatusSource; }
    if (Settings.SettingsNumber === 4) { SETTINGS_RANK_PRICE_2.RelayStatus = newStatus; SETTINGS_RANK_PRICE_2.RelayStatusSource = relayStatusSource; }
}

// This is executed if API did not respond properly. This is NOT considered as successful execution
function RunBackupHourRule(Settings) {

    // Check if settings has BackupHours property
    let hasBackupRules = false;
    for (let property in Settings) {
        if (property === "BackupHours") { hasBackupRules = true; }
    }

    // Return if not configured
    if (hasBackupRules === false) { print("This setting does not have BackupHours property. Relay name: " + Settings.RelayName); return; }

    // Check if current hour is listed in backup hours
    let currentHourIsListed = false;
    for (let i = 0; i < Settings.BackupHours.length; i++) {
        if (Settings.BackupHours[i] === currentHour) { currentHourIsListed = true; }
    }

    // When current hour has not been updated for some reason, it might be better to consider this as backup hour
    // Comment this line, if you don't like the behavior.
    if (currentHour === null || currentHour === "") { currentHourIsListed = true; }

    // Set Shelly relay according if hour is backup hour.
    print("Executing backup rule for relay: " + Settings.RelayName + " - Current hour: " + currentHour + " - Current hour is backup hour: " + JSON.stringify(currentHourIsListed));
    SetRelayStatusInShelly(Settings, currentHourIsListed, "backupHour");
}

// Get the current hour and put it in global variable
function UpdateCurrentHour(rounds) {

    Shelly.call("Shelly.GetStatus", "", function (res, rounds) {
        if (res.sys.time !== null) {
            currentHourUpdated = res.sys.time.slice(0, 2);  // f.ex. "21:34"
            if (rounds === 1) { currentHour = currentHourUpdated; }
        }
        else {
            currentHourUpdated = ""; // Time is null if Shelly does not have connection to time server
        }
    }, rounds);
}

// Initialize relay statuses if hour has changed
function InitializeRelaysIfHourHasChanged(rounds) {

    if (currentHour !== currentHourUpdated || rounds === 1) {
        // Update current hour
        currentHour = currentHourUpdated;

        // Skip relays which are not in use - set their "RelayExecuted" state to true.
        if (SETTINGS_PRICELIMIT_1.RelayIsInUse === true) { SETTINGS_PRICELIMIT_1.RelayExecuted = false } else { SETTINGS_PRICELIMIT_1.RelayExecuted = true; };
        if (SETTINGS_PRICELIMIT_2.RelayIsInUse === true) { SETTINGS_PRICELIMIT_2.RelayExecuted = false } else { SETTINGS_PRICELIMIT_2.RelayExecuted = true; };
        if (SETTINGS_RANK_PRICE_1.RelayIsInUse === true) { SETTINGS_RANK_PRICE_1.RelayExecuted = false } else { SETTINGS_RANK_PRICE_1.RelayExecuted = true; };
        if (SETTINGS_RANK_PRICE_2.RelayIsInUse === true) { SETTINGS_RANK_PRICE_2.RelayExecuted = false } else { SETTINGS_RANK_PRICE_2.RelayExecuted = true; };
    }
}

// Check if all relays are done for this hour.
function HasCurrentHourBeenDone() {

    if (SETTINGS_PRICELIMIT_1.RelayExecuted === true &&
        SETTINGS_PRICELIMIT_2.RelayExecuted === true &&
        SETTINGS_RANK_PRICE_1.RelayExecuted === true &&
        SETTINGS_RANK_PRICE_2.RelayExecuted === true) {
        print("Current hour is already done.");
        return true;
    }

    return false;
}

// Builds URL to call the API
function BuildUrl(Settings) {

    // Price limit URL
    if (Settings.Url === 1) {
        return "https://api.spot-hinta.fi/JustNow/" + Settings.PriceAllowed + "?region=" + Region + "&allowedDays=" + Settings.AllowedDays;
    }

    // Price and Rank limit URL
    if (Settings.Url === 2) {
        let url = "https://api.spot-hinta.fi/JustNowRank/" + Settings.Rank + "/" + Settings.PriceAllowed;
        url += "?maxPrice=" + Settings.MaxPrice;
        url += "&allowedDays=" + Settings.AllowedDays;
        url += "&boosterHours=" + Settings.BoosterHours;
        url += "&priorityHours=" + Settings.PriorityHours;
        url += "&priorityHoursRank=" + Settings.PriorityHoursRank;
        url += "&priceModifier=" + Settings.PriceModifier;
        url += "&region=" + Region;
        return url;
    }

    return "";
}

// Main timer to execute rules
Timer.set(30000, true, ExecuteRelayRules);
 

Mikki

Hyperaktiivi
Mitenkäs tämä skripti nyt pitäisi rakentaa, että saan eri päiville eri asetukset? Eli lauantaisin klo 01-06 olisi tarkoitus tehdä legionellakuumennus. Tällä tavalla se ajaa molemmat asetukset peräkkäin ja jälkimmäinen, eli tuo lauantaille asetettu, jää voimaan.

Äkkiä katsoen parametrit on oikein. Kun skriptin käynnistää, ajetaan tosiaan molemmat säännöt ja lauantain sääntö jää voimaan.

Mutta seuraavalla tunnilla joa ei ole lauantai, ei se sääntö tee mitään koska vastaus on palvelimelta varmasti "BadRequest" ja se sääntö kun edellisellä tunnilla oli jo BadRequest, ei lauantai sääntö tee mitään.

Eli vain ensimmäisellä tunnilla skripti toimii väärin. Sitten se menee rytmiinsä ja homman pitäisi pelittää oikein.
 
Back
Ylös Bottom