HomeAssistant ja sähköpörssiohjaus

jkoljo

Aktiivinen jäsen
Siitä mikä datahubista näkyy. Joka vartille on hinta ja kulutus. Mikä sille sitten oikea termi onkaan.
Varmaankin varttimittaus nyt kuitenkin vielä. Luulisin että vielä tämän kuun ajan jokainen vartti maksaa saman verran €/kWh. Toki kWh voi vaihdella ja vaihteleekin. Ensi kuussa myös €/kWh vaihtelee.
 
Totta. Mittari LadisGyr E450. Enpä arvannut että moiseen kykenisi, mutta näköjään pystyy. Ei ole HAN-porttia ja vaihto vasta ensi syksynä. Tosin en osaa kuvitella mitään istanssia mitä räpsytelisin vartin välein päälle ja pois. Pääasia että joku integraatio toimii. Näissä yhden ihmisen virityksissä on aina riskinsä useamman githubin sorsan ylläpitoon. Paras olisi kun tekisi itse ja määrittäisi backup palvelut myös.
 

hemaris

Aktiivinen jäsen
Totta. Mittari LadisGyr E450. Enpä arvannut että moiseen kykenisi, mutta näköjään pystyy. Ei ole HAN-porttia ja vaihto vasta ensi syksynä. Tosin en osaa kuvitella mitään istanssia mitä räpsytelisin vartin välein päälle ja pois. Pääasia että joku integraatio toimii. Näissä yhden ihmisen virityksissä on aina riskinsä useamman githubin sorsan ylläpitoon. Paras olisi kun tekisi itse ja määrittäisi backup palvelut myös.
Sama tilanne minulla. LadisGyr E350 pystyy myös 15 min mittarointiin. Ei ole HAN porttia. Mittari vaihdetaan parin vuoden sisällä, jos haluaa nopeammin, pitää maksaa 83€. Näin siis Helenillä.
 

-Teme-

Vakionaama
Totta. Mittari LadisGyr E450. Enpä arvannut että moiseen kykenisi, mutta näköjään pystyy. Ei ole HAN-porttia ja vaihto vasta ensi syksynä. Tosin en osaa kuvitella mitään istanssia mitä räpsytelisin vartin välein päälle ja pois. Pääasia että joku integraatio toimii. Näissä yhden ihmisen virityksissä on aina riskinsä useamman githubin sorsan ylläpitoon. Paras olisi kun tekisi itse ja määrittäisi backup palvelut myös.
Auton lataus on yksi minkä voi ohjata 6A - 16A ja keskeyttää helposti ihan varttiohjauksella
 
Auton lataus on yksi minkä voi ohjata 6A - 16A ja keskeyttää helposti ihan varttiohjauksella
Totta. Ei ole käytössä kun menee firman piikkiin. Lvv ja lattialämmitys suurimmat vartilla kannattavasti ohjattavat syöpöt. Voihan niitäkin räpsytellä jos tahtoo. Ikuisuusprojektina hommaan tuntuu olevan EMHASS, jota en vielä ole saanut viritettyä, mutta vahva usko on tulevaan lämmityskauteen. Helpottaisi kovasti ,luulen, vaikkei aurinkopaneleita olekaan.
 

MarjoC

Jäsen
Ja vielä pitää tarkistaa omasta sähkönmyyntiyhtiöstä, että muuttuuko sopimus varttihinnoitelluksi. Aika moni suurikin firma on ilmoittanut että ei ainakaan automaattisesti mene vartteihin, vaan tuntikeskiarvoilla mennään 1.10. jälkeenkintavasinpa

Ja vielä pitää tarkistaa omasta sähkönmyyntiyhtiöstä, että muuttuuko sopimus varttihinnoitelluksi. Aika moni suurikin firma on ilmoittanut että ei ainakaan automaattisesti mene vartteihin, vaan tuntikeskiarvoilla mennään 1.10. jälkeenkin,.
Tavasinpa nyt oman sopimuksen sopimusehtoja (vaihdoin kesällä Eletraan):

  1. Hinta perustuu Pohjoismaisen Nord Pool -sähköpörssin tuntikohtaiseen Suomen aluehintaan. Asiakkaalta laskutetaan jokaiselta tunnilta käytetystä energiasta kyseisen tunnin Suomen alueen spot-hinta lisättynä myyntisopimuksen mukaisella välityspalkkiolla (marginaali). Sähkön myyntihintaan (spot-hinta) lisätään sähköyhtiön marginaali (snt/kWh), joka ilmoitetaan erikseen.
Edellinen sopimus PKS, jossa oli huomautus varttihinnoitteluun siirtymisestä.

Mutta kysymys kuuluu mites koodi toimii 1.10? Haluaisin joka tapauksessa että energyboard pitäisi kirjaa kulutuksesta myös euroina lokakuussakin…
 

tet

Hyperaktiivi
Virallista hintaa et saa Nordpoolista ellet osta lisenssiä ja maksa siitä pari tonnia vuodessa.
Entso-e taitaa olla ainoa virallinen rajapinta josta hinnat voi hakea ilmaiseksi. Sielläkin hikkaa ajoittain
Eleringistä saa myös haettua suomen hinnat

Eleringiltä ei taida saada kuin meneillän olevan tunnin (vastaisuudessa vartin) hinnan, ei koko tulevaa vuorokautta?
 

Sukke

Aktiivinen jäsen
Oma mittari ei taivu vielä 15 minuutin taseeseen eli menee vielä 60 minuutin hinnoilla laskutus.

Täytyy varmaan ensi vaiheessa pitää automaatiot nykyisellään, mutta varmistaa, että saa laskettua tuntihinnat varttihinnoista automaation käyttöön.


En ole lainkaan innostunut päivittämään ohjauksia 15 minuutin hinnoilla. Tunti oli sopiva perusyksikkö MLP:n ohjaukseenkin, mutta 15 minuutin kanssa menee astetta haastavammaksi.
 

Jassu

Tulokas
Näillä näkymin nykyinen toteutus jää "tuntihintoihin" ja uusi versio tulee käyttämään vartteja. Eli jos haluaa pysyä tuntihinnoissa, niin ei tarvitse tehdä mitään. Ja uusi varttiversio SHF-paketista tulee viimeistään lokakuun alkupäivinä.
Ok, eli jos se että hinnoista lasketaan tuntikohtainen keskiarvo kelpaa, niin ei tarvitse tehdä mitään. Scripti osaa parseroida varteista tuntikeskiarvot jo nyt?


Kontaktoreillehan tuo tuntikohtainen säätely on hellävaraisempaa, kuin vartein välein lätkyttely, joten senkin pohjalta varmaan monille riittä tuo.

Itsellä muutama kymmenentuhatta riviä YAMLia nojaa aikalailla sekä 24 tunnin rankkaukseen (by SHF) että SHF:n antamaan tuntihintaan. On ilouutinen jos tuo ei räjähdä kuun vaihteessa :)
 

grendy

Vakionaama
Ok, eli jos se että hinnoista lasketaan tuntikohtainen keskiarvo kelpaa, niin ei tarvitse tehdä mitään. Scripti osaa parseroida varteista tuntikeskiarvot jo nyt?


Kontaktoreillehan tuo tuntikohtainen säätely on hellävaraisempaa, kuin vartein välein lätkyttely, joten senkin pohjalta varmaan monille riittä tuo.

Itsellä muutama kymmenentuhatta riviä YAMLia nojaa aikalailla sekä 24 tunnin rankkaukseen (by SHF) että SHF:n antamaan tuntihintaan. On ilouutinen jos tuo ei räjähdä kuun vaihteessa :)
Olisin kans erittäin tyytyväinen jos 24h ranking toimisi jatkossakin kuten nyt! Joku päivä tosiaan kun säätöhammasta kolottaa niin voi siirtyä seuraavaan versioon jossa on sitten ihanat 96 rankingia :D
 

ttk2

Aktiivinen jäsen
Olen laittanut sähköisen lämminvesivaraajan eristeen alle lämpötila-anturin ja säätänyt varaajan oman termostaatin maksimille. Tein tämän alunperin siksi, että pääsen lataamaan varaajaa kuumemmaksi silloin, kun aurinkosähköä menisi muuten huonolla hinnalla myyntiin.

Kun varaajan lämpötila ja sähkön hinta on tiedossa, niin kokeilin myös tehdä muuttuvaa tavoitelämpöä käyttövedelle. Tämä toimii pörssihinnan mukaan: kun on halpaa, ylilämmitetään. Kun muuttuva tavoitelämpötila saavutetaan, automaatio katkaisee lämmityksen, vaikka kesken rankin mukaan halvan tunnin.

Lämpötilan minimiksi on asetettu 51°C ja maksimiksi 70°C. Tavoitelämpötila muuttuu tuolla välillä hinnan mukaan. Huom: todellinen lämpötila varaajassa saattaa olla (ja onkin) näitä arvoja korkeampi - mittaus tapahtuu eristeen alta, mutta ei erillisestä anturitaskusta. Itselleni tämä tarkkuus riittää, kun absoluuttinen lämpötila ei kiinnosta yhtä paljon kuin se, että lämmintä vettä tulee tarpeeksi.

Template helper "target_water_temperature":

YAML:
{% set min_temp = 51 %}
{% set max_temp = 70 %}
{% set x = states("sensor.shf_electricity_price_now") | float * 100 %}
{% set y = max(min_temp, min(max_temp, 100 - 3.6 * x)) %}
{{ y|round(1) }}

En tiedä säästänkö vai häviänkö tätä käyttämällä, mutta tulipahan tehtyä ja on ollut käytössä jo yli vuoden. Ylilämmitys tietysti lisää häviötä, eli se on hukkaa (joka säteilee tekniseen tilaan lämpönä). Toisaalta tällä on todistetusti kokonaan vältetty varaajan lämmityksiä kalliina päivinä/tunteina, jotka seuraavat halpoja päiviä/tunteja. Oletuksena olen sallinut lämmityksen kahdella halvimmalla tunnilla. Ja aurinkosähkön ylimäärän annan mennä varaajaan ilman lämpötilan ylärajaa, jolloin varaajan oma termostaatti tulee lopulta vastaan mikäli energiaa vaan riittää.

Liekö varaajan oma maksimi 80°C ja tuo oma anturini ei koskaan näytä yli 72°C lämpötilaa, joten sen verran se mittaus heittää, ainakin skaalan yläpäässä.

Antureita oli ylimääräisenä laatikossa, samoin Wemos D1 mini, johon anturin sai kiinni (ja jossa esphome). Osien hinta ilman postikuluja lienee kympin luokkaa.

No mitäpä sitten? Ei mitään, mutta ajattelin kertoa, että näinkin sen voi tehdä. Näinä voimakkaasti muuttuvien hintojen aikoina voi ladata lämpöä halvempaan aikaan siinä toivossa, että tuleva kalliimpi vuorokausi (tai jopa useampi) voidaan kokonaan välttää. Säästö on aivan varmasti marginaalinen, mutta voi tällä yrittää maksimoida halpojen tuntien kulutusta ja toisaalta aurinkosähkön omaa kulutusta. Jos taas myydystä aurinkosähköstä saa korkeamman hinnan, varaajaa ei silloin ylilämmitetä.
 

Mikki

Hyperaktiivi
Mahtavaa... näyttää tosiaan toimivan suoraan. Hyvä jos ei mitään mennyt rikki. Nyt sitten kohti varttisiirtymää ja eiköhän siitä kuulla lähipäivinä. Kuuleman mukaan ei ole oikeastaan edes iso homma.
 

MarjoC

Jäsen
Tänään tuli Electralta ilmoitus, että huomenna siirrytään 15min sähköön.. :( Ja SHF:n kautta tuli keskiarvot. Hyviä ideoita mitä voi tehdä.. minulla on kaikkien laitteiden lämmitykset SHF:n takana, niin tarve olisi kyllä saada tuo varttihinta käytöön asap. :( Nordpoolista sain kyllä näkyviin huomisen hinnat 15 min välein, mutta kun kaikki automaatiot käyttää rank or price yhdistelmää. Esimerkiksi huomenna klo 7:ltä varttien ero noin 8.7 sentin ja 13,3 sentin välillä. kyllä kiinnoistaisi tuokin säästö ottaa talteen mikäli vain mahdollista
 
Viimeksi muokattu:

heebo1974

Aktiivinen jäsen
Tänään tuli Electralta ilmoitus, että huomenna siirrytään 15min sähköön.. :( Ja SHF:n kautta ei ole siirtynyt mitään hintoja huomiselle. Hyviä ideoita mitä voi tehdä.. minulla on kaikkien laitteiden lämmitykset SHF:n takana eli kaikki rikki :( Nordpoolista sain kyllä näkyviin huomisen hinnat
Kummallista. Minulla tuli hinnat ihan normaalisti ilman mitään muutoksia. Olet varmaan kokeillut käynnistää varmuudeksi uudelleen ? Tai ainakin ajanut sen hakuautomaation uudelleen ?
 

Mikki

Hyperaktiivi
Tänään tuli Electralta ilmoitus, että huomenna siirrytään 15min sähköön.. :( Ja SHF:n kautta ei ole siirtynyt mitään hintoja huomiselle. Hyviä ideoita mitä voi tehdä.. minulla on kaikkien laitteiden lämmitykset SHF:n takana eli kaikki rikki :( Nordpoolista sain kyllä näkyviin huomisen hinnat
Kannattaa tosiaan tehdä mitä Heebo tuossa ehdotti. Rajapinta on pystyssä kyllä, mutta päivän mittaan on pari päivitystä sinne tehty ja jos huonosti on sattunut sinun päivitys kohdalle, se on voinut epäonnistua.
 

kenzy

Jäsen
Hyvin toimi huomisen päivän tuntihintojen päivitys.

Näemmä varttihintojen päivitys Home Assistantiin onnistuu suhteellisen simppelisti muokkaamalla spot-price.yaml:n rest-sensoria tähän muotoon:

Koodi:
sensor:
  - platform: rest
    resource: https://api.spot-hinta.fi/TodayAndDayForward?HomeAssistant15Min=true
    name: SHF Electricity price
    unique_id: shf_electricity_price
    scan_interval: 604800 # 7 days, update logic is in the automation
    value_template: "OK" # static value as sensor is not updated regularly
    json_attributes:
      - "data"

Ja varttihinnat pamahataa vanhoille sensoreille ja kaikki 96 rankkia näkyy hienosti. Itse en käytä mitään muita noita spot-hinta.fi- integraation apusensoreita tai muita, oon koodaillut ne itse, niin tämä riittää toistaiseksi ainakin itselleni.

Kun tuo tiedosto on päivitetty, sitten vain tallennusta, Home assistantin pika-rebootti ja SHF Electricity price- ja SHF data- sensoreiden päivityksen triggeröinti esimerkiksi automaatiolla päivittää hinnat.

Jos käyttää nykyistä cheapest_period_lenght- helperiä halvimman yhtäjaksoisen tuntimäärän määritykseen ja haluaa, että vastaava toimii jatkossakin, mutta varttihinnoittelulla, niin spot-price.yamliin tarvii tehdä nämä muutokset:

Koodi:
    sequence:
      - variables:
          start_time: >
            {% set output = namespace(value=[], tmp = []) %}
            {% set data = state_attr("sensor.shf_data", "data") 
                | selectattr("Timestamp", "ge", start | as_timestamp) 
                | selectattr("Timestamp", "lt", stop | as_timestamp) 
                | list %}
            {% set quarters = quarters if quarters is number else quarters["quarters"] %}
            {% set quarters = min(quarters | int, data | length) %}
            {% for inval in data %}
              {% set output.tmp = data 
                  | selectattr("Timestamp", "ge", inval["Timestamp"]) 
                  | selectattr("Timestamp", "lt", inval["Timestamp"] + quarters*900) 
                  | list %}
              {% if output.tmp | length >= quarters %}
                {% set d = dict(inval, **{"PriceTmp": output.tmp | sum(attribute="TotalPrice") }) %}
                {% set output.value = output.value + [d] %}
              {% endif %}
            {%- endfor -%}
            {% set starttime = (output.value | sort(attribute="PriceTmp"))[0]["Timestamp"] %}
            {{ { "start" : starttime } }}
      - stop: Return variable
        response_variable: start_time
        enabled: true
    mode: parallel
    max: 100

Ja

Koodi:
    sequence:
      - variables:
          period_hours: >
            {{ states('input_number.shf_cheapest_period_lenght_h') | int }}
          quarters: >
            {{ (states('input_number.shf_cheapest_period_lenght_h') | int) * 4 }}
          start_time: >
            {% set output = namespace(value=[], tmp = []) %}
            {% set data = state_attr("sensor.shf_data", "data")
                | selectattr("Timestamp", "ge", start | as_timestamp)
                | selectattr("Timestamp", "lt", stop | as_timestamp)
                | list %}
            {% set quarters = [quarters|int, data|length] | min %}
            {% for inval in data %}
              {% set output.tmp = data
                  | selectattr("Timestamp", "ge", inval["Timestamp"])
                  | selectattr("Timestamp", "lt", inval["Timestamp"] + quarters*900)
                  | list %}
              {% if output.tmp | length >= quarters %}
                {% set d = dict(inval, **{"PriceTmp": output.tmp | sum(attribute="TotalPrice") }) %}
                {% set output.value = output.value + [d] %}
              {% endif %}
            {%- endfor -%}
            {% set starttime = (output.value | sort(attribute="PriceTmp"))[0]["Timestamp"] %}
            {{ { "start" : starttime } }}
      - stop: Return variable
        response_variable: start_time
        enabled: true
    mode: parallel
    max: 100

Niin automaatio hakee halvimman määritetyn peräkkäisen tuntimäärän tällä uudella varttihinnoittelulla.

En ole koodari, joten koodi ei välttämättä ole siistein mahdollinen, mutta näyttää toimivan.
 
Viimeksi muokattu:

Samppa

Ylläpitäjä
Ylläpidon jäsen
Testiin meni. Sellaisen olin havaitsevinani että rank now sensori ei päivittänyt tilaansa 22:45. Ehdin juuri 22:43 täräyttää muutoksen jälkeen HA:n uudelleen käyntiin ja jäi makaamaan tuo 22:30 alkaneen vartin rank sensoriin. Mutta en ole varma vaikuttiko tuo tiukalle mennyt startti asiaan. Nyt tasalta päivitti normaalisti. Itselläni tuo on muutamassa lämmitysautomaatiossa triggerinä, sillä olisi hyvä että toimisi oikein. Keinoäly meinasi tuosta sensorista seuraavaa:

Sensorin päivittyminen vain tasatunnein johtuu todennäköisesti siitä, että state:-kaavassa käytetään seuraavaa riviä:
YAML:
{% set n = state_attr('sensor.shf_data', 'data') | selectattr("Timestamp", "eq", now().replace(minute=0, second=0, microsecond=0) | as_timestamp) | first %}

Tämä hakee vain tasatunnin aikaleiman, eli esim. klo 14:00, 15:00 jne. → ei päivity 15 min välein.

Korjaus: Käytä viimeisintä mennyttä datapistettä

Korvaa tuo rivi tällä:
YAML:
{% set n = state_attr('sensor.shf_data', 'data') | selectattr("Timestamp", "lt", now() | as_timestamp) | list | last %}
Tämä hakee viimeisimmän datapisteen, joka on ennen nykyhetkeä — eli juuri se vartti, joka on käynnissä.

En alkanut nyt testaamaan tätä kun aiemmin iltapäivästä keinoäly ei oikein ollut kartalla näiden korjausehdotustensa kanssa kun testailin omatoimisesti noita varttihinnoittelumuutoksia. Pitää odotella tuonne vartin yli, niin näkee alkaako päivittymään normaalisti.
 
Viimeksi muokattu:

Samppa

Ylläpitäjä
Ylläpidon jäsen
Eipä se päivittynyt tuo shf_electricity_rank_now klo 23:15. Kokeilin tuota yllä mainittuä tekoällikorjausta ja vaikuttaisi että saattaa jopa toimia. Puolelta näkyy että jatkaako päivittymistään.

edit: sehän alkoi toimimaan. Eli joko yllä oleva korjaus tai jokin fiksumpi tarvitaan rank_now sensoriin:
1759264291825.png
 
Viimeksi muokattu:

-Teme-

Vakionaama
Nordpool HACS paketin sensori päivittää statuksen vain kerran tunnissa.
Jotta tuon saa toimimaan vartti hinnoilla pitää pari muutosta tehdä.
muutos 1: __init__py rivi 194
Python:
cb_new_hr = async_track_time_change(hass, new_hr, minute=0, second=0)
pitää muuttaa muotoon:
Python:
cb_new_hr = async_track_time_change(hass, new_hr, minute=[0, 15, 30, 45], second=0)
Toinen muutos sensor.py rivi 432
Python:
if item["start"] == start_of(local_now, "hour"):
    self._current_price = item["value"]
muutetaan muotoon:
Python:
if item["start"] <= local_now < item["end"]:
    self._current_price = item["value"]
Näillä toimii 15 min hinta päivitykset
Tein tuosta oman PR, katsotaan jos implementoidaan
 

Temez

Aktiivinen jäsen
Eipä se päivittynyt tuo shf_electricity_rank_now klo 22:15. Kokeilin tuota yllä mainittuä tekoällikorjausta ja vaikuttaisi että saattaa jopa toimia. Puolelta näkyy että jatkaako päivittymistään.

edit: sehän alkoi toimimaan. Eli joko yllä oleva korjaus tai jokin fiksumpi tarvitaan rank_now sensoriin:
katso liitettä 108875
Täytyypä laittaa tämä korjaukseen. Kiitos havainnosta @Samppa ! Testaaminen jäi aika vähälle, kun vapaa-aika on todella kortilla.
 

Samppa

Ylläpitäjä
Ylläpidon jäsen
Ensimmäiset varttiohjauksetkin on nyt takana. Lämminvesivaraajaa yöllä lämmiteltiin tunnin ajan kahdessa 15min ja yhdessä 30min pätkässä. Kaikki toiminut hienosti tähän asti :bileet:
1759291591227.png


Yksi mikä vähän pisti silmään on tuo control factor käyrä. Minulla tuo hienosäätää lattialämmitysten pyyntiä. Apex chart koodikin vaati vähän muutoksia, jotta sai vartin jaksoihin näkyviin kuvaajat. Luettavuus on nyt tällä jo vähän huono, pitää jossain kohtaa säätää resoluutiota jotenkin. Joka tapauksessa, aika teräviä liikkuja tekee tuo control factor. Pitää vähän seurailla sen toimintaa ja tehdä hienosäätöjä parametreihin. Toki kun perässä vain vastuskuormaa niin ei niin väliäkään terävillä muutoksilla. Aikamoisia hintapiikkejähän tuolla iltapäivässä oli. Hyvä vain jos ne kalliit vartit saa blokkailtua pois varmuudella. Jos tällä hienosäätäisi esim. ilpin pyyntiä, niin ei taitaisi ilppi pysyä perässä tämän taajuuden muutoksissa. :p
1759291983662.png
 
Viimeksi muokattu:

Temez

Aktiivinen jäsen
Testiin meni. Sellaisen olin havaitsevinani että rank now sensori ei päivittänyt tilaansa 22:45. Ehdin juuri 22:43 täräyttää muutoksen jälkeen HA:n uudelleen käyntiin ja jäi makaamaan tuo 22:30 alkaneen vartin rank sensoriin. Mutta en ole varma vaikuttiko tuo tiukalle mennyt startti asiaan. Nyt tasalta päivitti normaalisti. Itselläni tuo on muutamassa lämmitysautomaatiossa triggerinä, sillä olisi hyvä että toimisi oikein. Keinoäly meinasi tuosta sensorista seuraavaa:

Sensorin päivittyminen vain tasatunnein johtuu todennäköisesti siitä, että state:-kaavassa käytetään seuraavaa riviä:
YAML:
{% set n = state_attr('sensor.shf_data', 'data') | selectattr("Timestamp", "eq", now().replace(minute=0, second=0, microsecond=0) | as_timestamp) | first %}

Tämä hakee vain tasatunnin aikaleiman, eli esim. klo 14:00, 15:00 jne. → ei päivity 15 min välein.

Korjaus: Käytä viimeisintä mennyttä datapistettä

Korvaa tuo rivi tällä:
YAML:
{% set n = state_attr('sensor.shf_data', 'data') | selectattr("Timestamp", "lt", now() | as_timestamp) | list | last %}
Tämä hakee viimeisimmän datapisteen, joka on ennen nykyhetkeä — eli juuri se vartti, joka on käynnissä.

En alkanut nyt testaamaan tätä kun aiemmin iltapäivästä keinoäly ei oikein ollut kartalla näiden korjausehdotustensa kanssa kun testailin omatoimisesti noita varttihinnoittelumuutoksia. Pitää odotella tuonne vartin yli, niin näkee alkaako päivittymään normaalisti.
Koodi päätyi melkein tämmöisenään githubiin. Vaihdoin "less than" filtterin "less or equal" -vaihtoehtoon varuilta. Olin katsovinani laittamastasi kuvasta, että rank ei vaihtunut täsmälleen 23:15. Ehkä syynä on ollut jokin muu (HA:n reboot tai jotain).

Eli päivitetty versio samassa paikassa: https://github.com/T3m3z/spotprices2ha/tree/15min_prices
 

Samppa

Ylläpitäjä
Ylläpidon jäsen
Eilen klo 23:15 oli vielä korjaamatta tuo koodi, seurasin siinä että vaihtuuko rank. Sen jälkeen heti korjasin ja boottasin HA:n jolloin päivittyi myöhässä (HA käynnistyshetkellä). Sen jälkeen on kyllä päivittynyt tarkalleen aina vartin vaihtuessa. Otanpa kuitenkin virallisen koodin tuolta käyttöön. Nopeaa toimintaa, kiitoksia.
 

Temez

Aktiivinen jäsen
Eilen klo 23:15 oli vielä korjaamatta tuo koodi, seurasin siinä että vaihtuuko rank. Sen jälkeen heti korjasin ja boottasin HA:n jolloin päivittyi myöhässä (HA käynnistyshetkellä). Sen jälkeen on kyllä päivittynyt tarkalleen aina vartin vaihtuessa. Otanpa kuitenkin virallisen koodin tuolta käyttöön. Nopeaa toimintaa, kiitoksia.
Nyt kun asiaa tarkemmin pohdin, niin taitaa siinä nykyhetken aikaleimassa tosiaan olla millisekunnitkin mukana niin se less than toimii oikein hyvin.
 

Ondalf

Tulokas
Tuossapa Temezin muutokset + peak laskut oikein, oli dataslottien määrä mikä tahansa; eli vartti, tunti, päivähinnottelu... Sekä extrana mahdollinen datatyyppi "quarter", jota ei käytetä... Noh, tulipaha tehtyä.
Tän pitäis toimia suoraan, ko ymppää nää muutokset custom-components/nordpool pohjaan
Tai sitten nappaa filut täältä

Diff:
diff --git a/custom_components/nordpool/__init__.py b/custom_components/nordpool/__init__.py
index b2aca72..f9a7dca 100644
--- a/custom_components/nordpool/__init__.py
+++ b/custom_components/nordpool/__init__.py
@@ -191,7 +191,7 @@ async def _dry_setup(hass: HomeAssistant, config: ConfigType) -> bool:
             hass, new_day_cb, hour=0, minute=0, second=0
         )

-        cb_new_hr = async_track_time_change(hass, new_hr, minute=0, second=0)
+        cb_new_hr = async_track_time_change(hass, new_hr, minute=[0, 15, 30, 45], second=0)

         api.listeners.append(cb_update_tomorrow)
         api.listeners.append(cb_new_hr)
diff --git a/custom_components/nordpool/misc.py b/custom_components/nordpool/misc.py
index 864ffe9..ec4e108 100644
--- a/custom_components/nordpool/misc.py
+++ b/custom_components/nordpool/misc.py
@@ -8,6 +8,8 @@ import pytz
 from homeassistant.util import dt as dt_util
 from pytz import timezone

+from datetime import timedelta, datetime
+
 UTC = pytz.utc

 __all__ = [
@@ -55,8 +57,12 @@ def stock(d):
     return d.astimezone(stockholm_tz)


-def start_of(d, typ_="hour"):
-    if typ_ == "hour":
+def start_of(d: datetime, typ_="hour") -> datetime:
+    if typ_ == "quarter":
+        remainder = d.minute % 15
+        floored_d = d - timedelta(minutes=remainder)
+        return floored_d.replace(second=0, microsecond=0)
+    elif typ_ == "hour":
         return d.replace(minute=0, second=0, microsecond=0)
     elif typ_ == "day":
         return d.replace(hour=0, minute=0, microsecond=0)
@@ -70,9 +76,12 @@ def time_in_range(start, end, x):
         return start <= x or x <= end


-def end_of(d, typ_="hour"):
+def end_of(d: datetime, typ_="hour") -> datetime:
     """Return end our hour"""
-    if typ_ == "hour":
+    if typ_ == "quarter":
+        start = start_of(d, "quarter")
+        return start + timedelta(minutes=15, microseconds=-1)
+    elif typ_ == "hour":
         return d.replace(minute=59, second=59, microsecond=999999)
     elif typ_ == "day":
         return d.replace(hour=23, minute=59, second=59, microsecond=999999)
@@ -123,10 +132,11 @@ def extract_attrs(data) -> dict:
     items = [i.get("value") for i in data]

     if len(data):
+        SLOTS_PER_HOUR = len(data) / 24
         data = sorted(data, key=itemgetter("start"))
-        offpeak1 = [i.get("value") for i in data[0:8]]
-        peak = [i.get("value") for i in data[8:20]]
-        offpeak2 = [i.get("value") for i in data[20:]]
+        offpeak1 = [i.get("value") for i in data[0:int(8*SLOTS_PER_HOUR)]]
+        peak = [i.get("value") for i in data[int(8*SLOTS_PER_HOUR):int(20*SLOTS_PER_HOUR)]]
+        offpeak2 = [i.get("value") for i in data[int(20*SLOTS_PER_HOUR):]]

         d["Peak"] = mean(peak)
         d["Off-peak 1"] = mean(offpeak1)
diff --git a/custom_components/nordpool/sensor.py b/custom_components/nordpool/sensor.py
index aad5f03..1964eac 100644
--- a/custom_components/nordpool/sensor.py
+++ b/custom_components/nordpool/sensor.py
@@ -307,14 +307,17 @@ class NordpoolSensor(SensorEntity):
             _LOGGER.debug("No data for today, unable to set attrs")
             return

+        SLOTS_PER_HOUR = len(today) / 24
+
         self._average = mean(today)
         self._min = min(today)
         self._max = max(today)
-        self._off_peak_1 = mean(today[0:8])
-        self._off_peak_2 = mean(today[20:])
-        self._peak = mean(today[8:20])
         self._mean = median(today)

+        self._off_peak_1 = mean(today[0:int(8*SLOTS_PER_HOUR)])
+        self._off_peak_2 = mean(today[int(20*SLOTS_PER_HOUR):])
+        self._peak = mean(today[int(8*SLOTS_PER_HOUR):int(20*SLOTS_PER_HOUR)])
+
     @property
     def current_price(self) -> float:
         """This the current price for the hour we are in at any given time."""
@@ -429,7 +432,7 @@ class NordpoolSensor(SensorEntity):
         data = await self._api.today(self._area, self._currency)
         if data:
             for item in self._someday(data):
-                if item["start"] == start_of(local_now, "hour"):
+                if item["start"] <= local_now < item["end"]:
                     self._current_price = item["value"]
                     _LOGGER.debug(
                         "Updated %s _current_price %s", self.name, item["value"]

Verrokiksi tän päivän data tuosta sensorista YAML muodossa muutoksen jälkeen, joten voit verrata, miltä se näyttää ennen ja jälkeen, jos viel ajat wanhempaa koodia:
YAML:
average: 0.11832397849462366
off_peak_1: 0.05810903225806452
off_peak_2: 0.15369125
peak: 0.14660195652173913
min: 0.02738
max: 0.36552
mean: 0.11272

edit numero xyz: Onko jotain muita repoja tai ideoita, mitä pitäis korjata? Vilkasin nopsaan ton Temezin 15min repon läpi, ja periaatteessa tän nordpool plugarin mahtais kohtuu vähällä vaivalla saada muokattua toimiin ton suomalaisen palvelun kans... En tiä, onko siitä niinkään hyötyä, mutta eipähä pääsis nordpoolin sedät sättimään...
 
Viimeksi muokattu:

Samppa

Ylläpitäjä
Ylläpidon jäsen
Tätä virhettä näyttää nyt tulevan logiin. Ilmeisesti datamäärä nyt hieman paisunut, en tiedä onko jotain tehtävissä tai onko tästä ylipäätään haittaa?

  • State attributes for sensor.shf_electricity_price_now exceed maximum size of 16384 bytes. This can cause database performance issues; Attributes will not be stored
  • State attributes for sensor.shf_data exceed maximum size of 16384 bytes. This can cause database performance issues; Attributes will not be stored
 

kenzy

Jäsen
Eilen klo 23:15 oli vielä korjaamatta tuo koodi, seurasin siinä että vaihtuuko rank. Sen jälkeen heti korjasin ja boottasin HA:n jolloin päivittyi myöhässä (HA käynnistyshetkellä). Sen jälkeen on kyllä päivittynyt tarkalleen aina vartin vaihtuessa. Otanpa kuitenkin virallisen koodin tuolta käyttöön. Nopeaa toimintaa, kiitoksia.
Meikäläisellä rank antaa vain unavailablea buuttien jälkeenkin. Mahtaakohan tuossa olla joku cachehomma, jonka takia menee sensori solmuun... kaikki muut pelittää kyllä hyvin.

EDIT:

Tällä koodinpätkällä lähti itsellä rank toimimaan.

EDIT 2: Aiempi koodinpätkä tuotti pykälää väärän vartin rankin, korjattu koodi alla, kiitos heebo1974

Koodi:
  - sensor:
      - name: "SHF Rank now"
        unique_id: shf_electricity_rank_now
        availability: >
          {{ state_attr("sensor.shf_data", "raw") is not none }}
        state: >
          {% set data = state_attr("sensor.shf_data", "raw") %}
          {% set current = data | default([])
             | selectattr("DateTime", "le", now().isoformat())
             | list
             | last %}
        
          {% if current and 'Rank' in current %}
            {{ current.Rank | int(default=none) }}
          {% else %}
            none
          {% endif %}
 
Viimeksi muokattu:

Jassu

Tulokas
Nätisti toimii SHF laskien tuntikeskiarvoa, aivan kuten täällä lupailtiinkin.

Aloin miettimään olisiko tuohon SHF:aan jollain muuttujalla helposti tehtävissä valinta, että voisi valita haluaako käyttää 15 min hintoja, tai tunnin keskiarvoa. Mikä vielä parempaa, niin jos tuohon saisi 30 min keskiarvonkin lisättyä, joka toimisi kivasti välimallina?

Parasta olisi tietenkin jos tuolla olisi suoraan sensorit noille kaikille, niin voisi osaa laitteistoa ohjata HomeAssistantilla tunnin keskiarvolla (tai tuntipohjaisella rankkauksella) ja osaa sitten 15 min hinnalla (tai rankkauksella).
 

kenzy

Jäsen
Nätisti toimii SHF laskien tuntikeskiarvoa, aivan kuten täällä lupailtiinkin.

Aloin miettimään olisiko tuohon SHF:aan jollain muuttujalla helposti tehtävissä valinta, että voisi valita haluaako käyttää 15 min hintoja, tai tunnin keskiarvoa. Mikä vielä parempaa, niin jos tuohon saisi 30 min keskiarvonkin lisättyä, joka toimisi kivasti välimallina?

Parasta olisi tietenkin jos tuolla olisi suoraan sensorit noille kaikille, niin voisi osaa laitteistoa ohjata HomeAssistantilla tunnin keskiarvolla (tai tuntipohjaisella rankkauksella) ja osaa sitten 15 min hinnalla (tai rankkauksella).
Esimerkiksi jos lisäät tällaisen koodin pätkän spot-price.yamliin:

Koodi:
  - sensor:
      - name: "SHF 30min average price"
        unique_id: shf_30min_avg_price
        unit_of_measurement: "EUR/kWh"
        availability: >
          {{ state_attr("sensor.shf_data", "raw") is not none }}
        state: >
          {% set data = state_attr("sensor.shf_data", "raw") %}
          {% if data %}
            {# Etsi kuluvan vartin rivi #}
            {% set now_time = now() %}
            {% set current = data
               | selectattr("DateTime", "le", now_time.isoformat())
               | selectattr("DateTime", "gt", (now_time - timedelta(minutes=15)).isoformat())
               | list
               | first %}
            {% if current %}
              {# Etsi seuraava vartti #}
              {% set idx = data.index(current) %}
              {% set next_entry = data[idx + 1] if idx + 1 < data | length else current %}
              {% set avg_price = ((current.PriceWithTax | float) + (next_entry.PriceWithTax | float)) / 2 %}
              {{ avg_price | round(5) }}
            {% else %}
              none
            {% endif %}
          {% else %}
            none
          {% endif %}

Saat 30min keskihintasensorin, joka laskee kuluvan vartin + tulevan vartin keskihintaa.

Ja tällä kuluvan tunnin keskihinta

Koodi:
  - sensor:
      - name: "SHF Current hour average price"
        unique_id: shf_current_hour_avg_price
        unit_of_measurement: "EUR/kWh"
        availability: >
          {{ state_attr("sensor.shf_data", "raw") is not none }}
        state: >
          {% set data = state_attr("sensor.shf_data", "raw") %}
          {% if data %}
            {# Määritetään kuluvan tunnin alku ja loppu #}
            {% set now_time = now() %}
            {% set hour_start = now_time.replace(minute=0, second=0, microsecond=0) %}
            {% set hour_end = hour_start + timedelta(hours=1) %}

            {# Valitaan kaikki rivit kuluvan tunnin sisällä #}
            {% set hour_data = data
               | selectattr("DateTime", "ge", hour_start.isoformat())
               | selectattr("DateTime", "lt", hour_end.isoformat())
               | list %}

            {% if hour_data | length > 0 %}
              {% set avg_price = (hour_data | map(attribute="PriceWithTax") | map("float") | sum) / hour_data | length %}
              {{ avg_price | round(5) }}
            {% else %}
              none
            {% endif %}
          {% else %}
            none
          {% endif %}

EDIT 2:

Toinen vaihtoehto on tietysti lisätä nämä suoraan tuon electricity price now- sensorin attribuuteiksi

Koodi:
  - sensor:
      - name: SHF Electricity price now
        unique_id: shf_electricity_price_now
        unit_of_measurement: &energy_price_unit €/kWh # Change this to c/kWh if you prefer cents
        device_class: monetary
        state: '{{ (state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "lt", now() | as_timestamp) | list)[-1]["TotalPrice"] | round(4) }}'
        availability: '{{ state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "ge", now() | as_timestamp ) | list | length > 0 }}'
        attributes:
          data: '{{ state_attr("sensor.shf_data", "data") }}'
          all_prices: '{{ state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "ge", today_at("0:00") | as_timestamp) | map(attribute="TotalPrice") |list }}'
          today_prices: '{{ state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "ge", today_at("0:00") | as_timestamp) | selectattr("Timestamp", "lt", today_at("23:59") | as_timestamp) | map(attribute="TotalPrice") |list }}'
          tomorrow_prices: '{{ state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "ge", today_at("23:59") | as_timestamp) | map(attribute="TotalPrice") |list }}'
          today_min: "{{ this.attributes.today_prices | default([0]) | min }}"
          today_avg: "{{ this.attributes.today_prices | default([0]) | average | round(4) }}"
          today_max: "{{ this.attributes.today_prices | default([0]) | max }}"
          tomorrow_min: "{{ this.attributes.tomorrow_prices | default([0], true) | min }}"
          tomorrow_avg: "{{ this.attributes.tomorrow_prices | default([0], true) | average | round(4) }}"
          tomorrow_max: "{{ this.attributes.tomorrow_prices | default([0], true) | max }}"
          current_30min_avg: >
            {% set now_ts = now() | as_timestamp %}
            {% set data_30 = state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "ge", now_ts) | selectattr("Timestamp", "lt", now_ts + 30*60) | list %}
            {% if data_30 | length > 0 %}
              {{ (data_30 | map(attribute="TotalPrice") | map("float") | sum / data_30 | length) | round(5) }}
            {% else %}
              0
            {% endif %}
          current_60min_avg: >
            {% set now_ts = now() | as_timestamp %}
            {% set data_60 = state_attr("sensor.shf_data", "data") | selectattr("Timestamp", "ge", now_ts) | selectattr("Timestamp", "lt", now_ts + 60*60) | list %}
            {% if data_60 | length > 0 %}
              {{ (data_60 | map(attribute="TotalPrice") | map("float") | sum / data_60 | length) | round(5) }}
            {% else %}
              0
            {% endif %}

Jolloin saadaan kutsuttua

Koodi:
{{ state_attr('sensor.shf_electricity_price_now', 'current_30min_avg') }}
{{ state_attr('sensor.shf_electricity_price_now', 'current_60min_avg') }}

Ja nämä vaikkapa ApexChartsiin mukaan

Koodi:
type: custom:apexcharts-card
apex_config:
  legend:
    show: true
  xaxis:
    labels:
      datetimeFormatter:
        year: ddd
        month: ddd
        day: ddd
        hour: HH
      rotate: -45
      rotateAlways: true
graph_span: 48h
experimental:
  color_threshold: true
show:
  last_updated: false
header:
  show: true
  show_states: true
  colorize_states: true
span:
  start: day
yaxis:
  - id: Hinta
    min: 0
    decimals: 2
    apex_config:
      forceNiceScale: true
    opposite: false
now:
  show: true
  label: Nyt
series:
  - entity: sensor.shf_electricity_price_now
    name: 15min price
    yaxis_id: Hinta
    show:
      in_header: false
      extremas: true
      legend_value: false
    type: column
    color: green
    float_precision: 2
    unit: c/kWh
    opacity: 1
    data_generator: |
      return entity.attributes.data.map((d, index) => {
        return [d["Timestamp"]*1000, d["TotalPrice"]*100];
      });
    color_threshold:
      - value: 0
        color: 368f39
      - value: 10
        color: a3b34d
      - value: 15
        color: ffd57e
      - value: 20
        color: f18c56
      - value: 25
        color: de425b
  - entity: sensor.shf_electricity_price_now
    name: 15min price
    color: green
    show:
      in_header: true
      in_chart: false
    data_generator: |
      return [[Date.now(), parseFloat(entity.state) * 100]];
    unit: c/kWh
  - entity: sensor.shf_electricity_price_now
    name: 30min average
    color: blue
    show:
      in_header: true
      in_chart: false
    data_generator: |
      return [[Date.now(), entity.attributes.current_30min_avg * 100]];
    unit: c/kWh
  - entity: sensor.shf_electricity_price_now
    name: 60min average
    color: orange
    show:
      in_header: true
      in_chart: false
    data_generator: |
      return [[Date.now(), entity.attributes.current_60min_avg * 100]];
    unit: c/kWh
  - entity: sensor.shf_rank_now
    name: Rank
    show:
      in_header: true
      in_chart: false
 

Liitteet

  • Screenshot 2025-10-01 at 14.36.48.png
    Screenshot 2025-10-01 at 14.36.48.png
    109,3 KB · Katsottu: 59
Viimeksi muokattu:

Tekniikkatulitaloon

Aktiivinen jäsen
Tätä virhettä näyttää nyt tulevan logiin. Ilmeisesti datamäärä nyt hieman paisunut, en tiedä onko jotain tehtävissä tai onko tästä ylipäätään haittaa?

  • State attributes for sensor.shf_electricity_price_now exceed maximum size of 16384 bytes. This can cause database performance issues; Attributes will not be stored
  • State attributes for sensor.shf_data exceed maximum size of 16384 bytes. This can cause database performance issues; Attributes will not be stored
Samaa virhettä täällä puskee. Haittaakohan tuo menoa?

Meikäläisellä rank antaa vain unavailablea buuttien jälkeenkin. Mahtaakohan tuossa olla joku cachehomma, jonka takia menee sensori solmuun... kaikki muut pelittää kyllä hyvin.

EDIT:

Tällä koodinpätkällä lähti itsellä rank toimimaan.

Koodi:
  - sensor:
      - name: "SHF Rank now"
        unique_id: shf_electricity_rank_now
        availability: >
          {{ state_attr("sensor.shf_data", "raw") is not none }}
        state: >
          {% set data = state_attr("sensor.shf_data", "raw") %}
          {% if data %}
            {% set current = data
               | selectattr("DateTime", "ge", now().isoformat())
               | list
               | first %}
            {% if current %}
              {{ current["Rank"] | int }}
            {% else %}
              none
            {% endif %}
          {% else %}
            none
          {% endif %}
Täällä myös rank näytti "Unavailable" ja kenzyn koodin pätkällä alkoi toimimaan. Vaihdoin siis tuolta spot-price.yaml tiedostosta rank sensorin määrityksen.

1759310012214.png


Menee vähän tihrustamiseksi nyt varttien kanssa mikä hinta milloinkin on. 30 minuutin jakso voisi olla mukavempi silmäillä ja voisi olla nimenomaan tunnin ensimmäinen ja jälkimmäinen puolisko nykytiedon valossa. Mintenkäs tuon saisi koodattua? :hmm:
 
Viimeksi muokattu:

kenzy

Jäsen
Samaa virhettä täällä puskee. Haittaakohan tuo menoa?


Täällä myös rank näytti "Unavailable" ja kenzyn koodin pätkällä alkoi toimimaan. Vaihdoin siis tuolta spot-price.yaml tiedostosta rank sensorin määrityksen.

katso liitettä 108892

Menee vähän tihrustamiseksi nyt varttien kanssa mikä hinta milloinkin on. 30 minuutin jakso voisi olla mukavempi silmäillä ja voisi olla nimenomaan tunnin ensimmäinen ja jälkimmäinen puolisko nykytiedon valossa. Mintenkäs tuon saisi koodattua? :hmm:
Hetken aikaa AI:n kanssa keskusteltuani, kokeilepa tätä:

Koodi:
type: custom:apexcharts-card
apex_config:
  legend:
    show: true
  xaxis:
    labels:
      datetimeFormatter:
        year: ddd
        month: ddd
        day: ddd
        hour: HH
      rotate: -45
      rotateAlways: true
graph_span: 48h
experimental:
  color_threshold: true
show:
  last_updated: false
header:
  show: true
  show_states: true
  colorize_states: true
span:
  start: day
yaxis:
  - id: Hinta
    min: 0
    decimals: 2
    apex_config:
      forceNiceScale: true
    opposite: false
now:
  show: true
  label: Nyt
series:
  - entity: sensor.shf_electricity_price_now
    yaxis_id: Hinta
    show:
      in_header: false
      extremas: true
      legend_value: false
    type: column
    color: lightgray
    float_precision: 2
    unit: c/kWh
    opacity: 1
    data_generator: |
      const data = entity.attributes.data;
      const result = [];
   
      // Muutetaan timestampit Date-objekteiksi
      data.forEach(d => d.ts = new Date(d.Timestamp * 1000));
   
      // Ryhmitellään keskiarvoiksi 30min välein (:00 ja :30)
      let group = [];
      let currentHalfHour = null;
   
      data.forEach(d => {
        // määritetään nykyinen puolen tunnin ryhmä
        const minutes = d.ts.getMinutes();
        const halfHour = minutes < 30 ? 0 : 30;
        const groupKey = new Date(d.ts.getFullYear(), d.ts.getMonth(), d.ts.getDate(), d.ts.getHours(), halfHour, 0, 0).getTime();
   
        if (currentHalfHour !== groupKey) {
          if (group.length > 0) {
            // lasketaan keskiarvo ja lisätään tulokseen
            const avg = group.reduce((sum, item) => sum + item.TotalPrice, 0) / group.length;
            result.push([currentHalfHour, avg * 100]);
          }
          currentHalfHour = groupKey;
          group = [];
        }
        group.push(d);
      });
   
      // viimeinen ryhmä
      if (group.length > 0) {
        const avg = group.reduce((sum, item) => sum + item.TotalPrice, 0) / group.length;
        result.push([currentHalfHour, avg * 100]);
      }
   
      return result;
    color_threshold:
      - value: 0
        color: 368f39
      - value: 10
        color: a3b34d
      - value: 15
        color: ffd57e
      - value: 20
        color: f18c56
      - value: 25
        color: de425b
 

Liitteet

  • Screenshot 2025-10-01 at 12.36.28.png
    Screenshot 2025-10-01 at 12.36.28.png
    69,8 KB · Katsottu: 59
Back
Ylös Bottom