Tekijä: Juha Mujunen

  • Case: oma talo — Osa 10: Aurinko ja teho Node-REDissä

    Tämä on kymmenes osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi hintaohjauksen päätöslogiikka Node-REDissä. Tässä osassa tarkastellaan kahta toisiinsa kytkeytyvää kokonaisuutta: aurinkoinvertterin tehonrajoitusta negatiivisilla hinnoilla ja vaihevirtojen reaaliaikaista valvontaa.


    Aurinkopaneeli tuottaa sähköä aina kun aurinko paistaa. Se ei kysy onko hinta hyvä tai huono. Ilman ohjausta kaikki ylijäämä menee verkkoon — myös silloin kun hinta on negatiivinen ja jokainen myyty kilowattitunti maksaa omistajalle.

    Tämä oli EnergyHubin yksi keskeisimmistä motivaatioista: rakentaa logiikka joka reagoi negatiivisiin hintoihin automaattisesti, rajoittaa vientiä älykkäästi ja ohjaa ylijäämä ensin omaan käyttöön.

    Aurinkoylijäämä — mitä se tarkoittaa käytännössä

    Aurinkoylijäämä on se osa tuotannosta joka ylittää talon hetkellisenkulutuksen. Jos aurinko tuottaa 8 kW ja talo kuluttaa 2 kW, ylijäämä on 6 kW — se menee verkkoon ellei sitä ohjata muualle.

    EnergyHubissa ylijäämä lasketaan reaaliajassa:

    javascript

    s.solarExcessW = Math.max(0, s.pvPowerW - Math.max(0, s.gridPowerW))
    s.isSolarExcess = s.solarExcessW > s.solarExcessThresholdW  // oletusarvo 2000 W

    gridPowerW on positiivinen kun ostetaan verkosta ja negatiivinen kun myydään. Kaava ottaa tämän huomioon: jos verkosta ostetaan (gridPowerW > 0), ylijäämää ei ole vaikka aurinko tuottaisi.

    Ylijäämä ohjataan ensisijaisesti omaan kulutukseen — EV-lataukseen tai lämpöpumpun boostiin. Vasta sen jälkeen, ja vain jos hinta on negatiivinen, rajoitetaan invertterin tehoa.

    PV-rajoitus negatiivisilla hinnoilla

    Kun spot-hinta laskee alle p_negative_price-kynnyksen ja aurinko tuottaa yli 500 W, Priority Resolver laskee sopivan rajoitusprosentin:

    javascript

    if (s.isNegativePrice && s.pvPowerW > 500) {
        const ownLoad = Math.max(300, s.pvPowerW + Math.min(0, s.gridPowerW));
        const pct = Math.max(10, Math.min(95,
            Math.round((ownLoad / s.pvPowerW) * 100)
        ));
        decisions.pvCurtailPct = pct;
    }

    ownLoad on talon hetkellinen omakäyttö — kuinka paljon aurinkosähköä kuluu itse. Rajoitusprosentti on tämän suhde invertterin nimellistehoon (15 kW). Jos talo kuluttaa 3 kW, rajoitus asetetaan 20 %:iin — invertteri saa tuottaa enintään 3 kW jotta vienti minimoidaan.

    Normaalitilassa invertteri on asetettu 110 %:iin — Sungrowin tapa ilmaista ”ei rajoitusta, käytä kaikki mitä paneeleilla on”. Kun negatiivinen hinta aktivoituu, rajoitus lasketaan dynaamisesti ja asetetaan esimerkiksi 24 %:iin. Kun hinta palaa normaaliksi, palataan 110 %:iin.

    Rajoituslaskennassa minimiarvo on 10 % eikä 0 %. Täysi sammutus aiheuttaa invertterin uudelleenkäynnistyksen ja voi johtaa ohjausongelmiin — 10 % on turvallinen alaraja joka pitää invertterin aktiivisena. Maksimiarvo rajoituslaskennassa on 95 % — tämä estää tilanteen jossa laskettu rajoitusarvo heiluisi juuri 100 %:n tuntumassa pienten kulutusvaihteluiden mukana. Täyteen tehoon palataan aina 110 %:n kautta, ei 100 %:n.

    Modbus-ohjaus käytännössä

    Komento kulkee Node-REDistä HA:lle MQTT:n kautta: energyhub/command/pv/curtail_pct: 24. HA vastaanottaa sen ja kirjoittaa Modbus-rekistereihin.

    Sungrow SG15RT -invertterissä rajoitus tapahtuu kahdella rekisterillä:

    • Rekisteri 5006 — Power Limitation Switch. Tämä on bittikenttä, ei yksinkertainen 0/1-kytkin. SG15RT:llä se on pysyvästi tilassa 170 (”Enabled”) — invertteri hyväksyy rajoituskomennot eikä rekisteriin tarvitse koskea.
    • Rekisteri 5007 — rajoitusarvo (0–1100, yksikkö 0,1 % nimellistehosta. Tähän rajoitus kirjoitetaan.

    24 % rajoitus tarkoittaa arvoa 240 rekisterissä 5007. Rajoitus aktivoituu pelkällä tällä kirjoituksella — 5006 pysyy koko ajan tilassa 170. Täyteen tehoon palataan kirjoittamalla 5007:ään arvo 1100 (= 110 %).

    Tämä on Sungrow-spesifinen toteutus — SMA, Fronius, ABB ja Huawei SUN2000 käyttävät omia rekisterikarttojaan, mutta SunSpec-standardia tukevat laitteet jakavat yhteisen osoitteiston. Rekisteritason yksityiskohdat on kuvattu tarkemmin ohjeessa Sungrow Modbus + tehonrajoitus.

    DC-potentiaaliaukko — mitä tapahtuu invertterin sisällä

    25.5.2026 tehdyssä testissä havaittiin jotain odottamatonta. Kun invertteri rajoitettiin 24 %:iin, MPPT-jännite nousi 501 voltista 544 volttiin — nousu noin 43 volttia. Virta laski vastaavasti 11,7 ampeerista 7,0 ampeeriin.

    Tämä paljastaa miten invertteri toteuttaa rajoituksen: se ei leikkaa AC-puolelta vaan siirtää paneelien toimintapisteen pois maksimitehopisteestä (MPP). Invertteri nostaa DC-jännitettä tarkoituksella, jolloin paneelit tuottavat vähemmän virtaa ja siten vähemmän tehoa.

    Käytännön seuraus: kun invertteri on rajoitustilassa, DC-rekisterit eivät kerro saatavilla olevaa paneelipotentiaalia — ne kertovat vain invertterin valitseman toimintapisteen. Testissä todellinen saatavilla oleva paneeliteho oli noin 11 kW, mutta rajoitustilan DC-teho näytti vain noin 3,8 kW. Jos haluaa tietää paljonko aurinkoa ”oikeasti” olisi saatavilla, pitää tehdä lyhyt testipulssi jossa rajoitus nostetaan hetkellisesti 110 %:iin ja mitataan toteutuva teho.

    30 päivän historiadatasta laskettu normaali MPPT-jännite on 474–515 V (keskiarvo 494 V, hajonta 20,8 V). Rajoituksen aikana mitattu 544 V on yli kaksi standardipoikkeamaa normaalin yläpuolella päiväaikaan — tilastollisesti selvästi poikkeava tapahtuma.

    Taloudellinen realismi

    Negatiivisten hintojen aiheuttama tappio on tällä hetkellä Suomessa pieni mutta kasvava.

    Vuonna 2025 tehdyn analyysin tulokset olivat yllättävän maltilliset. Negatiivisilla hinnoilla myytiin noin 651 kWh sähköä 115 tunnin aikana — kokonaistappio oli noin 2 euroa. Painotettu myyntihinta näillä tunneilla oli -0,344 snt/kWh. Suhteessa kokonaisuuteen — koko vuoden myyntitulo oli noin 359 € 8 632 myydystä kilowattitunnista — negatiivisten hintojen vaikutus oli pieni.

    Mutta analyysi paljasti tärkeän asian: suurin osa tappiosta syntyi lievästi negatiivisilla hinnoilla, ei äärimmäisissä piikeissä. Yksittäiset -500 EUR/MWh -hetket ovat dramaattisia mutta harvinaisia. Lievästi negatiivisia tunteja on paljon enemmän — ja niiden määrä kasvaa joka vuosi. Nolla- tai negatiivisen hinnan tunteja oli Suomessa vuonna 2024 peräti 994 — lähes 41 vuorokautta.

    Yksittäisenä päivänä 25.5.2026 tappio ilman rajoitusta olisi ollut noin 0,5 euroa. Trendi on selvä: rajoituksen taloudellinen hyöty kasvaa suoraan negatiivisten tuntien määrän mukana. Jos 2026 jatkaa samaa trendiä, vuositason tappio ilman rajoitusta voi olla helposti 10–30 euroa 15 kW järjestelmällä. Ei suuri summa — mutta automatisoitu suojaus ei myöskään maksa mitään käytön jälkeen.

    Safety Guardian — vaihevirtojen reaaliaikainen valvonta

    PV-rajoitus on proaktiivinen toimenpide — se reagoi hintaan ennen kuin ongelma syntyy. Safety Guardian on reaktiivinen — se reagoi vaihevirtoihin kun ne jo ovat korkealla.

    Safety Guardian pyörii erillisessä Node-RED-flow’ssa ja kuuntelee energyhub/telemetry/phases-topicia joka päivittyy 10 sekunnin välein. Se ei odota minuuttisykliä.

    Logiikka on yksinkertainen mutta tehokas:

    javascript

    const maxA = Math.max(Math.abs(l1), Math.abs(l2), Math.abs(l3));
    const warnLimit = 30;   // A → peak_protection
    const reduceLimit = 36; // A → kuormanpudotus
    const tripLimit = 40;   // A → safety trip
    const hysteresis = 25;  // A → paluu normal
    
    if (maxA >= tripLimit) {
        // SAFETY TRIP — välitön toiminta
        return safety_trip: true
    }
    if (maxA >= warnLimit && mode !== 'peak_protection') {
        // Pyydä moodinvaihtoa
        return mode: 'peak_protection'
    }
    if (mode === 'peak_protection' && maxA < hysteresis) {
        // Paluu normaaliin
        return mode: 'normal'
    }

    Nämä kynnykset eivät olleet alusta asti tällaiset. Suunnitteluvaiheessa lähdettiin konservatiivisista arvoista — varoitus jo 22,5 ampeerissa ja laukaisu 24,5 ampeerissa, mikä on 90 % ja 98 % 25 ampeerin sulakkeesta. Käytännön käytössä nämä osoittautuivat liian herkiksi: järjestelmä reagoi normaaleihin kulutuspiikkeihin turhan usein. Kynnyksiä nostettiin tuotantokäyttöön arvoihin 30 / 36 / 40 A, jotka antavat enemmän pelivaraa normaalille kuormalle mutta puuttuvat silti tilanteeseen selvästi ennen sulakkeen todellista laukeamista.

    Hystereesi on tärkeä: moodiin ei palata heti kun virta laskee alle varoitusrajan — vasta kun se laskee selvästi alle sen. Tämä estää moodin flippaamisen edestakaisin pienten vaihteluiden mukana.

    Toimintaperiaate näkyi käytännössä jo aikaisin: kun L1 nousi varoitusrajan yli, Guardian laukaisi peak_protection-moodin ja EV-lataus estettiin. Kun virta laski hystereesirajan alle, Guardian palautti normal-moodin. Koko tapahtuma hoitui ilman käyttäjän toimenpiteitä — juuri kuten reaktiivisen suojan kuuluu toimia.

    Aurinko ja sulakkeet — sama ongelma eri suunnista

    Aurinkoinvertteri ja sulakevalvonta ratkaisevat saman ongelman eri suunnista.

    Sulakevalvonta katsoo kuinka paljon verkosta ostetaan ja pudottaa kuormia kun raja lähestyy. Aurinkoinvertteri vaikuttaa siihen kuinka paljon verkkoon myydään ja siten epäsuorasti myös siihen paljonko talo nettona kuluttaa.

    Yhdessä ne muodostavat kaksisuuntaisen tehonhallinnan: Safety Guardian estää liian suuren oston, PV-rajoitus minimoi turhan myynnin. Kumpikaan ei yksin riitä — yhdessä ne pitävät talon tehotasapainon hallinnassa sekä ostopuolella että myyntipuolella.

    Mitä testistä opittiin — arkkitehtuurin synty

    Käytännön testi vuonna 2025 paljasti jotain odottamatonta. Kun invertterin tehoa alettiin rajoittaa Modbusin kautta, huomattiin että rajoitus ei ole irrallinen toiminto — se muuttaa koko järjestelmän dynamiikan.

    Kun aurinkotuotantoa rajoitettiin, EV-latauksen logiikka muuttui. Lämmityksen optimointi muuttui. Ylijäämäenergian hyödyntäminen muuttui. Useat automaatiot alkoivat vaikuttaa toisiinsa tavalla jota kukaan ei ollut suunnitellut.

    Johtopäätös oli selvä: invertterin tehorajoitus ei voi olla yksittäinen automaatio. Se vaatii keskitetyn tilannekuvan, prioriteetit, ohjauksen omistajuuden ja eri järjestelmien koordinoinnin. Tarvitaan EnergyHub-arkkitehtuuria.

    Tästä havainnosta syntyi koko sarjan kantava teema:

    Energian optimointi ei ole yksittäinen automaatio-ongelma — se on arkkitehtuuri-ongelma.

    Miksi tämä ei onnistu pelkällä ajastimella

    Yksinkertaisempi ratkaisu olisi ajastaa invertteri pois päältä kun hinta on negatiivinen. Se toimisi — mutta karkeasti.

    Ongelma on että negatiivinen hinta ei tarkoita automaattisesti että rajoitus on järkevää. Jos talo kuluttaa paljon — kiuas käy, EV latautuu, lämpöpumppu boostaa — invertteri voi tuottaa täydellä teholla eikä mitään mene verkkoon. Rajoitus olisi turha.

    EnergyHubin logiikka laskee reaaliaikaisesti paljonko vientiä oikeasti on ja rajoittaa sen verran kuin tarpeen. Ei enemmän. Tämä on tarveperustaista ohjausta — ei kynnysohjausta.


    Seuraavaksi: Osa 11 — Konfliktit. Kun aurinko tuottaa, hinta on halpa ja sulake uhkaa yhtä aikaa — miten järjestelmä ratkaisee kolmen tavoitteen ristiriidan.

    Tekninen toteutus: PV-rajoitus Modbus-ohjauksella on kuvattu tarkemmin ohjeessa Sungrow Modbus + tehonrajoitus.

  • Case: oma talo — Osa 9: Hintaohjauksen flow Node-REDissä

    Tämä on yhdeksäs osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi MQTT-viestintäkerros — miten tieto kulkee komponenttien välillä. Tässä osassa katsotaan mitä Node-RED tekee sillä tiedolla: miten hintaohjauksen päätöslogiikka rakentuu käytännössä.


    Node-RED on EnergyHubin optimointikerros. Se vastaanottaa kaiken telemetrian, ylläpitää tilannekuvaa ja tekee päätökset. Mutta Node-RED ei ole yksi iso automaatio — se on kolme erillistä flow’ta joilla on omat vastuunsa.

    State Collector kerää kaiken datan ja pitää tilannekuvan ajan tasalla. Se ei tee päätöksiä.

    Decision Engine ajaa päätöslogiikan minuutin välein. Se kysyy: mitä pitää tehdä, kenen vuoro on, miksi.

    Safety Guardian valvoo sulakerajoja 10 sekunnin välein ja reagoi välittömästi. Se ei optimoi — se suojelee.

    Tässä osassa keskitytään Decision Engineen — siihen miten hintatieto muuttuu päätöksiksi.

    State Collector — tilannekuvan ylläpito

    Ennen kuin Decision Engine voi päättää mitään, sen täytyy tietää missä ollaan. State Collector kuuntelee kaikkia energyhub/#-topiceja ja päivittää flow-kontekstin aina kun uusi viesti saapuu.

    Global context toimii Node-REDin yhteisenä tilamuistina — kaikki flow’t voivat lukea ja kirjoittaa siihen. Avainten etuliitteet kertovat mistä on kyse: m_ on mitattu arvo (Layer 2 -abstraktio), c_ capability- tai ohjaussignaali, p_ keskitetty parametri, sys_ järjestelmän tila. Se sisältää kaiken tarvittavan:

    javascript

    global.get('m_grid_power_w')       // verkkoteho tällä hetkellä (mitattu)
    global.get('m_pv_power_w')         // aurinkotuotanto (mitattu)
    global.get('m_spot_price_eur_kwh') // spot-hinta
    global.get('m_phase_l1_a')         // L1-vaihevirta
    global.get('m_hp_tap_water_top_c') // käyttöveden lämpötila
    global.get('sys_operating_mode')   // järjestelmän tila
    global.get('c_ev_charge_allowed')  // onko EV-lataus sallittu
    global.get('p_fuse_limit_a')       // sulakearvo (parametri)

    State Collector myös laskee johdetut arvot jotka helpottavat päätöksentekoa:

    javascript

    s.maxPhaseA = Math.max(Math.abs(l1), Math.abs(l2), Math.abs(l3))
    s.solarExcessW = Math.max(0, pvPowerW - Math.max(0, gridPowerW))
    s.isSolarExcess = s.solarExcessW > solarExcessThresholdW
    s.isCheapPrice = s.spotPrice < cheapPriceThreshold
    s.isNegativePrice = s.spotPrice < negativePriceThreshold
    s.isPeakLoad = s.maxPhaseA > peakLoadThresholdA
    s.isTapWaterLow = s.tapWaterC < tapWaterMinC

    Nämä boolean-arvot tekevät päätöslogiikasta luettavaa. s.isSolarExcess on helpompi ymmärtää kuin pvPowerW – Math.max(0, gridPowerW) > 2000. Kaikki kynnysarvot — solarExcessThresholdW, cheapPriceThreshold, peakLoadThresholdA ja muut — luetaan keskitetystä EH Parameters -nodesta, ei kovakoodattuna jokaiseen funktioon. Tämä on sarjassa aiemmin esitelty periaate: yksi paikka jossa kaikki säädettävät arvot elävät.

    Decision Engine — päätöslogiikka

    Decision Engine käynnistyy minuutin välein. Se lukee tilannekuvan, ajaa Priority Resolverin ja julkaisee komennot MQTT:hen.

    Rakenne on yksinkertainen:

    [Inject: 1 min] → [Lue tila] → [Priority Resolver] → [Julkaise komennot]
                                            ↓
                                   [Kirjaa päätökset]

    Priority Resolver on koko järjestelmän tärkein funktio. Se käy tilanteen läpi kiinteässä järjestyksessä — ensimmäinen osuma ratkaisee. Järjestys ei ole sattuma: ylimpänä ovat turvallisuus ja erikoistilat, alimpana talous.

    Priority Resolver käytännössä

    Koodi etenee hierarkkisesti. Turvallisuus ensin, talous viimeisenä. Tasoja on käytännössä useampi kuin tässä näytetään — täysi ketju on emergency → manual_override → fallback → peak protection → vacation → normaali optimointi. Manual_override palauttaa tyhjän päätöksen (ei muutoksia mihinkään) ja fallback ajaa minimiparametreilla, kun tilannekuva on puutteellinen. Keskitytään tässä turvallisuuden ja talouden kannalta oleellisimpiin tasoihin.

    Taso 1: Emergency

    javascript

    if (s.safetyTrip || s.mode === 'emergency') {
        return {
            ev: false,
            hpMode: 'block',
            hpImmersion: false,    // lisävastus pois
            pvCurtailPct: null,    // aurinko saa tuottaa vapaasti
            saunaAllowed: false,   // kiuas estetty
            comfortWheel: 17       // lattialämmitys minimiin
        };
    }

    Emergency-tilassa ei optimoida. Kaikki ei-kriittiset kuormat alas — EV, lisävastus, kiuas — ja lattialämmitys minimiin. Aurinko jätetään vapaaksi, koska se vain pienentää verkkokuormaa.

    Taso 2: Peak protection

    javascript

    if (s.mode === 'peak_protection' || s.isPeakLoad) {
        decisions.ev = false;
        decisions.saunaAllowed = false;
        decisions.hpImmersion = false;
        decisions.hpMode = s.hpCompressorPct > 60 ? 'block' : 'normal';
        decisions.pvCurtailPct = null; // ei rajoiteta — aurinko pienentää verkkokuormaa
        decisions.comfortWheel = 17;
        return decisions;
    }

    Sulake uhkaa — pudota kuormat. Huomaa: PV:tä ei rajoiteta koska se pienentää verkkokuormaa, ei kasvata sitä.

    Taso 3: Normaali optimointi

    Tässä haarassa tehdään varsinainen hintaohjaus. Jokainen kuorma käsitellään erikseen, ja turvallisuustarkistukset tulevat aina ensin.

    EV-latauksen päätös alkaa kahdella suojalla ennen hintalogiikkaa:

    javascript

    if (s.phasesStale) {
        decisions.ev = false;  // F15: ilman tuoretta vaihevirtatietoa EV voisi
                               // ylikuormittaa sulakkeen huomaamatta
    } else if (s.evManualOverride) {
        decisions.ev = true;   // manuaaliohitus pakottaa latauksen päälle
    } else if (!s.cap.evAllowed) {
        decisions.ev = false;  // capability estää
    } else if (s.mode === 'solar_maximize' && s.isSolarExcess) {
        decisions.ev = true;   // aurinkoylijäämä — käytä itse
    } else if (s.isVeryCheap) {
        decisions.ev = true;   // alle 1 snt/kWh — lataa aina
    } else if (s.isCheapPrice || s.mode === 'cheap_energy') {
        decisions.ev = true;   // halpa hetki — lataa
    } else if (s.mode === 'guest') {
        decisions.ev = true;   // vierastila — mukavuus prioriteetiksi
    } else {
        // normaali tilanne — laske onko perusteltua, viiveiden kanssa
        decisions.ev = s.isCheapPrice || s.isSolarExcess;
    }

    Ensimmäinen tarkistus on vaihevirtadatan tuoreus. Jos Safety Guardian ei ole saanut tuoretta vaihevirtatietoa, EV-lataus estetään — ilman tätä lataus voisi ylikuormittaa sulakkeen ilman että järjestelmä huomaa. Tämä sama suoja toistuu vielä komentokerroksessa viimeisenä porttina.

    Lämpöpumpun päätös:

    javascript

    if (s.mode === 'solar_maximize' || s.mode === 'cheap_energy') {
        decisions.hpMode = s.cap.hpBoostAllowed ? 'boost' : 'normal';
    } else if (s.isNegativePrice && s.cap.hpBoostAllowed) {
        decisions.hpMode = 'boost';  // negatiivinen hinta — käytä kaikki itse
    } else if (s.spotPrice > evuThreshold && s.cap.hpEvuAllowed) {
        decisions.hpMode = 'block';  // kallis — säästä (EVU)
    } else {
        decisions.hpMode = 'normal';
    }

    Tässä block ei tarkoita lämpöpumpun täyttä sammutusta vaan Thermian EVU-signaalia (SG1=ON, SG2=OFF), jolla pumppu lykkää lämmitystä kalliin hinnan yli. EVU:lla on noin 5 minuutin reagointiviive — kyse on lykkäyksestä, ei kovasta katkaisusta. Muiden valmistajien pumpuissa (Nibe, Vaillant, Mitsubishi) vastaava toiminto löytyy SG Ready -rajapinnasta hieman eri muodossa. evuThreshold tulee EH Parametersista (oletus 0,15 €/kWh).

    PV-rajoituksen päätös:

    javascript

    if (s.isNegativePrice && s.pvPowerW > pvCurtailMinW) {
        const ownLoad = Math.max(300, s.pvPowerW + Math.min(0, s.gridPowerW));
        const pct = Math.max(10, Math.min(95,
            Math.round((ownLoad / s.pvPowerW) * 100)
        ));
        decisions.pvCurtailPct = pct;
    } else {
        decisions.pvCurtailPct = null;  // ei rajoitusta
    }

    PV-rajoitus lasketaan dynaamisesti omakäytön perusteella — ei kiinteällä prosentilla. Rajoitusprosentti on invertterin nimellistehosta (tässä 15 kW). Jos talo kuluttaa 3 kW ja aurinko tuottaa 12 kW, omakäyttö on 3 kW — rajoitus asetetaan noin 20 %:iin nimellistehosta (3 kW / 15 kW) jotta vienti minimoidaan mutta oma kulutus katetaan edelleen aurinkosähköllä.

    Tarveperustainen ajoitus — ei pelkkiä kynnyksiä

    Pelkkä hintakynnys ei riitä älykkääseen ohjaukseen. EV-latauksen tapauksessa järjestelmä ei vain tarkista ”onko hinta alle X” — se arvioi onko lataus ylipäätään tarpeen ja milloin se kannattaa tehdä.

    Käytännössä tämä tarkoittaa että Decision Engine arvioi:

    1. Onko tarve? — onko EV kytkettynä, tarvitseeko akku latausta
    2. Mikä on aikahorisontti? — milloin auto tarvitaan seuraavan kerran
    3. Mikä on halvin hetki tässä ikkunassa? — ei välttämättä juuri nyt

    Sama periaate lämpöpumpussa: boosti ei laukea vain koska hinta on halpa — se laukea jos käyttövesi on viilenemässä tai huomisen sääennuste lupaa kylmää. Halpa hinta on mahdollisuus, tarve on perustelu.

    Tässä on keskeinen ero tavalliseen kynnysohjaukseen: järjestelmä optimoi aikaikkunan sisällä eikä reagoi yksittäiseen hetkeen. Optimointi ei ole vain ON/OFF-päätös — se on ajoitusongelma.

    Komennot ulos — ja miksi ne ovat yksinkertaisia

    Kun Priority Resolver on tehnyt päätöksensä, komennot julkaistaan MQTT:hen:

    javascript

    node.send({ topic: 'energyhub/command/ev/allowed', payload: String(d.ev), retain: false });
    node.send({ topic: 'energyhub/command/hp/mode', payload: d.hpMode, retain: false });
    if (d.pvCurtailPct !== null) {
        node.send({ topic: 'energyhub/command/pv/curtail_pct', payload: String(d.pvCurtailPct), retain: false });
    }

    Komennot ovat tarkoituksella yksinkertaisia, eikä niitä julkaista retain-lipulla — komento on hetkellinen, kuten osassa 8 käytiin läpi. Node-RED ei tiedä miten EV-lataus teknisesti toteutetaan — se tietää vain haluaako se sallia sen vai ei. HA hoitaa loput: Shelly-releen ohjauksen, Modbus-kirjoitukset, automaatioiden laukeamisen.

    Tämä on kerrosmallin konkretiaa: optimointikerros päättää, integraatiokerros toteuttaa.

    Päätösten kirjaus — observability

    Jokainen Decision Enginen päätös kirjataan observability-topiciin:

    javascript

    node.send({
        topic: 'energyhub/observability/decisions',
        payload: {
            ts: new Date().toISOString(),
            mode: s.mode,
            grid_w: s.gridPowerW,
            pv_w: s.pvPowerW,
            spot_eur: s.spotPrice,
            max_phase_a: s.maxPhaseA,
            decisions: { ev: d.ev, hp_mode: d.hpMode, pv_curtail_pct: d.pvCurtailPct },
            reasons: r  // miksi kukin päätös tehtiin
        },
        retain: false
    });

    reasons-objekti on se mikä tekee tästä oikeasti hyödyllistä. Se ei kirjaa vain mitä tehtiin — se kirjaa miksi.

    javascript

    reasons.ev = 'PEAK: vaihevirta 23.8A'
    reasons.hpMode = 'Negatiivinen hinta: -0.001 EUR/kWh'
    reasons.pvCurtail = 'Negatiivinen hinta: rajoitus 24%'

    Kun jälkikäteen kysytään miksi EV ei latautunut tiistaina klo 14 — vastaus on tallessa InfluxDB:ssä täsmällisesti kirjattuna.

    Mitä Decision Engine ei tee

    On yhtä tärkeää ymmärtää mitä Decision Engine ei tee kuin mitä se tekee.

    Se ei ohjaa releitä suoraan. Se ei kirjoita Modbus-rekistereihin. Se ei tiedä mikä laite on fyysisesti kytkettynä mihinkin. Kaikki tämä on HA:n vastuulla.

    Se ei myöskään säilytä päätöshistoriaa optimointimielessä — jokainen minuuttisykli lukee tilannekuvan uudelleen eikä ”muista” mitä se päätti tunti sitten edukseen tai haitakseen. Mutta täysin tilaton se ei ole, eikä saa olla: Decision Engine ylläpitää eksplisiittistä tilakonetta hystereesiä, viiveitä ja toteumahavaintoja varten. EV-latauksella on minimiajoaika ja päälle/pois-viiveet, jotta lataus ei pätki minuutin välein hinnan heilahdellessa. DHW-boostilla on oma elinkaarensa (käynnistys → vahvistus mitatusta tehosta → palautus). Erillinen havaintokerros vertaa jokaisella syklillä mitä järjestelmä päätti siihen mitä fyysisesti tapahtui. Tämä tila tekee logiikasta vakaata — ilman sitä sama tilannekuva tuottaisi nopeaa edestakaista vaihtelua. Ennakoitavuus ei siis synny tilattomuudesta vaan siitä, että tila on hallittua ja kirjattua: hystereesi, operating modet ja capability-flagien hitaampi muutosrytmi vaimentavat heilahtelun.


    Seuraavaksi: Osa 10 — Aurinko ja teho Node-REDissä. Miten PV-tuotanto, aurinkoylijäämä ja tehonrajoitus toimivat yhdessä päätöksenteossa.

    Tekninen toteutus: Node-RED-flowt ja Priority Resolver -koodi on kuvattu ohjeessa HA:n valmistelu integraatiokerrokseksi sekä Node-RED flowt — rakenne ja toteutus.

  • Case: oma talo — Osa 8: MQTT — viestintäkerros

    Tämä on kahdeksas osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi hinta ja sulakkeet — kaksi keskeisintä rajoitetta jotka ohjaavat energiapäätöksiä. Tässä osassa tarkastellaan sitä mikä pitää kaikki kerrokset yhdessä: MQTT-viestintäkerros.


    Kenttälaitteet kommunikoivat Home Assistantin kanssa omilla protokollillaan — Sungrow-invertteri Modbus TCP:llä, Thermia-lämpöpumppu Modbus RTU:lla, P1-mittari omalla rajapinnallaan. Mutta ylempien kerrosten — Home Assistantin ja Node-REDin — välinen viestintä tarvitsi oman väylän. Vaihtoehtoina olisi ollut suorat API-kutsut, tietokantakyselyt tai jaettu muisti. EnergyHubissa valittiin MQTT.

    Valinta ei ole sattuma. MQTT on suunniteltu juuri tähän: hajautettuun, epäluotettavaan ympäristöön jossa komponentit käynnistyvät ja sammuvat eri tahtiin, yhteydet katkeavat ja palautuvat, ja jokaisen komponentin pitää selviytyä itsenäisesti myös silloin kun muut eivät vastaa.

    Mitä MQTT on ja miksi se toimii tässä

    MQTT on kevyt julkaise/tilaa-protokolla. Lähettäjä julkaisee viestin brokerille tiettyyn topiciin — vastaanottajat jotka ovat tilanneet kyseisen topicin saavat viestin. Lähettäjä ja vastaanottaja eivät tiedä toisistaan. Ne tietävät vain topicin.

    Tämä löyhä kytkentä on keskeinen ominaisuus. Home Assistant ei lähetä komentoa suoraan Node-REDille — se julkaisee mittauksen MQTT-brokerille. Node-RED lukee sen kun haluaa. Jos Node-RED on poissa käynnissä puoli tuntia ja palaa takaisin, se lukee viimeisimmän retain-viestin ja jatkaa siitä. HA:n ei tarvitse tietää kuka viestin kuluttaa.

    Sama toimii toiseen suuntaan. Node-RED julkaisee komennon energyhub/command/ev/allowed: true — HA kuuntelee topicia ja reagoi. Node-RED ei kutsu HA:n API:a suoraan. Se ei tiedä miten HA on konfiguroitu tai mitä automaatiota siellä pyörii.

    EnergyHubissa MQTT-brokeri on Mosquitto, joka pyörii samalla Lenovo ThinkCentrellä Docker-kontissa. Se on yksinkertainen, luotettava ja tarkoitukseen sopiva.

    Topicrakenne — neljä suuntaa

    EnergyHubin MQTT-topicit on jaettu neljään kategoriaan jotka kuvaavat tiedon kulkusuunnan ja tarkoituksen:

    energyhub/telemetry/    HA → Node-RED    mittaukset, hinnat, lämpötilat
    energyhub/command/      Node-RED → HA    ohjauskomennot laitteille
    energyhub/system/       molemmat suunnat järjestelmätilan viestit
    energyhub/observability/ Node-RED → kaikki  päätösloki auditointia varten

    Telemetria on HA:n julkaisemaa dataa. Se sisältää kaiken mitä Node-RED tarvitsee päätöksentekoon: verkkotehon, vaihevirrat, aurinkotuotannon, spot-hinnan, lämpöpumpun lämpötilat, capability-flagit ja operating moodin. Telemetria lähtee minuutin välein, vaihevirrat 10 sekunnin välein Safety Guardiania varten.

    Komennot ovat Node-REDin päätöksiä. Ne kulkevat aina samaan suuntaan — Node-REDistä HA:lle. HA ei tee omia optimointipäätöksiä, se vain toteuttaa komennot.

    Järjestelmäviestit ovat poikkeustapauksia: safety trip, moodinvaihto, watchdog-signaalit. Nämä voivat kulkea molempiin suuntiin ja niillä on korkein prioriteetti.

    Observability on päätösloki — ei ohjausta, ei komentoja. Jokainen Node-REDin päätös kirjataan tähän topiciin syineen. InfluxDB tallentaa ne ja Grafana visualisoi.

    Ilman observabilityä automaatio näyttää satunnaiselta. Päätös tapahtui joskus, jossain YAML:ssa, jollain ehdolla. Observability tekee päätöksestä jäljitettävän: ”Tiistaina klo 14:05 EV-lataus estettiin koska peak_protection oli aktiivinen ja L1-vaihe oli 23,8 ampeeria.” Tämä on se kerros joka erottaa hallittavan järjestelmän hallitsemattomasta.

    Retain — viimeinen tunnettu totuus

    Retain on MQTT:n tärkeimpiä ominaisuuksia hajautetussa järjestelmässä. Kun viesti julkaistaan retain-lipulla, broker tallentaa sen. Uusi tilaaja saa sen heti liittyessään — vaikka alkuperäinen julkaisija olisi sammunut tunteja sitten.

    EnergyHubissa retain-politiikka on tarkoin harkittu:

    TopicRetainSyy
    energyhub/telemetry/*❌ eiMittaus on tuore tai sitä ei ole — vanha arvo ei saa jäädä elämään brokeriin
    energyhub/system/capabilities✅ kylläOperating mode ja capability-tila säilyvät uudelleenkäynnistyksen yli
    energyhub/command/*❌ eiKomento on hetkellinen — vanha komento ei saa toistua
    energyhub/system/safety_trip✅ kylläTurvatila säilyy kunnes se eksplisiittisesti nollataan
    energyhub/observability/*❌ eiLokidata ei tarvitse persistenssiä brokkerissa

    Retain-viestit ovat EnergyHubin muisti — mutta vain järjestelmätilan osalta. Kun Node-RED käynnistyy uudelleen huollon jälkeen, se ei aloita tyhjältä pöydältä tilan osalta: se lukee capabilities- ja safety_trip-topicit retain-viesteistä ja tietää heti missä moodissa ollaan ja onko turvatila päällä. Mittaukset se sen sijaan odottaa tuoreina — ensimmäinen telemetriasykli täydentää kuvan sekunneissa.

    Tässä on nimittäin sudenkuoppa, joka tekee telemetrian retainista vaarallisen: retain-viesti voi jäädä brokeriin väärässä tilassa. Jos vanha mittaus jäisi retain-viestiksi, uusi tilaaja saisi sen heti — eikä voisi tietää onko se sekunnin vai tunnin vanha. Siksi mittaukset eivät ole retained. Sama ilmiö koskee tilatietoa, mutta siellä se on hallittu: jos safety_trip julkaistiin true-arvolla mutta järjestelmä ei koskaan nollaa sitä, se jää retain-viestiksi joka toistuu jokaiselle uudelle tilaajalle. EnergyHubissa opittiin tämä kantapään kautta — Safety Guardian laukaisi turvatilan, mutta retain-viesti jäi true:ksi vaikka tilanne oli jo ohitse. Ratkaisu: turvatilan nollaus on eksplisiittinen toimenpide, ei automaattinen.

    QoS — kuinka tärkeä viesti on

    MQTT:ssä on kolme palvelutasoa:

    QoS 0 — lähetä ja unohda. Ei kuittausta, ei uudelleenlähetystä. Viesti voi kadota.

    QoS 1 — toimita vähintään kerran. Broker kuittaa vastaanoton. Viesti voi tulla useammin kuin kerran.

    QoS 2 — toimita täsmälleen kerran. Neljän viestin kättely, korkein luotettavuus.

    EnergyHubissa QoS-valinta on tarkoituksellinen:

    • Telemetria: QoS 0. Jos yksi minuuttimittaus katoaa, seuraava tulee pian. Parempi nopeus kuin varmuus.
    • Komennot: QoS 1. Komento menee perille vähintään kerran. Jos se tulee vahingossa kahdesti, ei haittaa — ”salli EV-lataus” kahdesti tuottaa saman lopputuloksen kuin kerran.
    • Safety trip: QoS 1. Turvatilan viesti on kriittinen ja sen pitää mennä perille. QoS 2 olisi teknisesti puhtain valinta, mutta käytännössä kotijärjestelmässä QoS 1 yhdistettynä retain-lippuun ja tilapohjaiseen logiikkaan on riittävä — ja yksinkertaisempi.

    MQTT ja vikasietoisuus

    MQTT:n löyhä kytkentä tekee järjestelmästä vikasietoisen — mutta ei automaattisesti. Vikasietoisuus pitää suunnitella.

    Broker kaatuu: kaikki kommunikaatio lakkaa. Laitteet jäävät viimeiseen ohjattuun tilaan. Mosquitto käynnistyy Docker-kontin mukana automaattisesti — tyypillinen katkos on sekunteja.

    Node-RED käynnistyy uudelleen: se lukee retain-viesteistä viimeisimmän tilan ja jatkaa. Ensimmäinen minuuttisykli päivittää tilanteen täysin.

    HA käynnistyy uudelleen: Node-RED odottaa uusia telemetriaviestejä. Safety Guardian saa vaihevirtatiedot vasta kun HA alkaa julkaista uudelleen — tämä on tiedostettu rajoite.

    Verkkoyhteys katkeaa: jos Mosquitto ja Node-RED ovat samalla koneella, ne kommunikoivat paikallisesti riippumatta internetyhteydestä. Nordpool-hintadata ei päivity, mutta viimeisin tunnettu hinta säilyy Node-REDin omassa kontekstissa.

    Topicit käytännössä

    Koko EnergyHubin MQTT-liikenne on tarkasteltavissa yhdellä komennolla:

    bash

    mosquitto_sub -h localhost -t "energyhub/#" -v

    Tämä on yksi järjestelmän tärkeimmistä debuggaustyökaluista. Kun jokin ei toimi, ensimmäinen tarkistus on katsoa mitä MQTT:ssä liikkuu. Onko telemetria päivittymässä? Lähettääkö Node-RED komentoja? Onko safety_trip jäänyt retain-viestiksi?

    Käytännön esimerkki 25.5.2026: Safety Guardian laukaisi safety_trip: true kun L1-vaihe ylitti 24,5 ampeeria. Viesti kulki energyhub/system/safety_trip-topiciin QoS 1:lla ja retain-lipulla. HA vastaanotti sen ja siirtyi emergency-moodiin. Kun tilanne raukesi, false-viesti lähetettiin eksplisiittisesti. Koko tapahtumaketju näkyi reaaliajassa mosquitto_sub-komennolla.

    MQTT ei ole ratkaisu kaikkeen

    MQTT sopii erinomaisesti asynkroniseen, tapahtumaohjattuun viestintään. Se ei sovi kaikkeen.

    Modbus-kommunikaatio kenttälaitteiden kanssa tapahtuu HA:n sisällä — ei MQTT:n kautta. Sungrow-invertteri ja Thermia-lämpöpumppu puhuvat Modbusia, ja HA toimii Modbus-masterina. Tämä on integraatiokerroksen sisäinen asia joka ei näy MQTT-väylässä.

    Historiadatan tallennus on InfluxDB:n vastuulla — ei MQTT:n. MQTT on viestintäväylä, ei tietokanta. Observability-topicin viestit tallennetaan InfluxDB:hen, mutta MQTT ei pidä niistä kirjaa pidempään kuin retain-viesti kestää.

    Näiden rajojen pitäminen selkeinä on osa arkkitehtuurin hallittavuutta. MQTT tekee yhden asian hyvin: välittää viestejä löyhästi kytkettyjen komponenttien välillä. Se ei yritä tehdä enempää.


    Seuraavaksi: Osa 9 — Hintaohjauksen flow Node-REDissä. Miten päätöslogiikka rakentuu käytännössä ja miten Priority Resolver tekee valintansa.

    Tekninen toteutus: MQTT-topicrakenne ja Mosquitto-konfiguraatio on kuvattu ohjeessa HA:n valmistelu integraatiokerrokseksi sekä Node-RED flowt — rakenne ja toteutus.

  • Case: oma talo — Osa 7: Hinta ja sulakkeet

    Tämä on seitsemäs osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi HA:n rooli ja entiteettimalli — miten data organisoidaan niin että se pysyy hallittavana. Tässä osassa tarkastellaan kahta keskeisintä rajoitetta jotka ohjaavat kaikkia energiapäätöksiä: sähkön hintaa ja sulakerajoja.


    Kaksi asiaa rajoittaa kotitalouden energiankäyttöä enemmän kuin mikään muu: kuinka paljon sähkö maksaa juuri nyt, ja kuinka paljon virtaa sulake sietää. Ne ovat luonteeltaan täysin erilaisia — hinta on taloudellinen rajoite, sulake on fyysinen rajoite. Mutta päätöksenteossa ne toimivat yhdessä ja niillä on selkeä prioriteetti: sulake voittaa aina.

    Spot-hinta — mistä se tulee ja mitä se oikeasti maksaa

    Pohjoismaisilla sähkömarkkinoilla spot-hinta määräytyy tunneittain Nord Pool -pörssissä. Hinta julkaistaan seuraavalle päivälle yleensä klo 13 aikoihin. EnergyHubissa Nordpool-integraatio hakee hinnat automaattisesti ja tallentaa ne HA:han.

    Mutta spot-hinta ei ole se hinta jonka kuluttaja maksaa. Todellinen ostohinta on monimutkaisempi:

    ostohinta = spot × 1,255 (ALV 25,5 %)
               + siirtomaksu (€/kWh)
               + marginaali (€/kWh)

    EnergyHubissa tämä lasketaan kanonisessa mittauskerroksessa m_buy_price_eur_kwh-sensorissa. Node-RED käyttää tätä arvoa — ei raakaa spot-hintaa. Tämä on tärkeä ero: optimointilogiikka vertaa todellista ostohintaa kynnysarvoihin, ei pörssihintaa.

    Myyntihinta on rakenteeltaan yksinkertaisempi kuin ostohinta. Se on aina spot-hinta ilman ALV:ia ja ilman siirtomaksua — nämä kuuluvat ostajan puolelle. Jotkut sähkön ostajat veloittavat pienen provision tai palvelumaksun joka vähennetään spot-hinnasta, mutta se on tyypillisesti marginaalinen. Negatiivisella spot-hinnalla myyntihinta on negatiivinen: jokaisesta verkkoon myydystä kilowattitunnista maksetaan.

    Hintakynnykset ja tarveperustainen optimointi

    Pelkät hintakynnykset ovat karkea ohjausmekanismi. ”Jos hinta on alle X, lataa auto” toimii yksinkertaisessa tapauksessa — mutta se ei kysele onko lataus tarpeen, onko akku jo täynnä tai onko jokin muu kuorma tärkeämpi juuri nyt.

    EnergyHubissa hintakynnykset ovat osa laajempaa logiikkaa: ensin kysytään onko toimenpide tarpeellinen, sitten etsitään sille sopiva ajankohta annetun aikajakson sisällä. Lämpöpumppua ei boostata vain koska hinta on halpa — se boostataan jos käyttöveden lämpötila on laskemassa tai jos huominen ennuste lupaa kylmää. EV-lataus ei käynnisty vain koska hinta alittaa kynnyksen — se ajoitetaan kyseisen latausjakson halvimpiin hetkiin tarpeen ja lähtöajan puitteissa.

    Optimointi ei ole vain ON/OFF-päätös — se on ajoitusongelma. Järjestelmä kysyy: mitä tarvitaan, milloin se viimeistään pitää tehdä, ja mikä on halvin hetki tehdä se tässä aikaikkunassa. Hintakynnykset toimivat tässä mallissa signaalina eikä absoluuttisena laukaisimena. Ne kertovat missä tilanteessa toimenpide on taloudellisesti perusteltua — mutta tarve ja ajoitus ratkaisevat lopullisen päätöksen.

    EnergyHubissa on kolme hintakynnystä. Kaikki ovat säädettäviä parametreja — ei hardkoodattuja lukuja.

    Negatiivinen hinta (p_negative_price, oletusarvo -0,02 EUR/kWh)

    Kun spot-hinta laskee alle tämän kynnyksen, verkkoon myynti on haitallista. Jokainen myyty kilowattitunti maksaa. Tässä tilanteessa EnergyHub rajoittaa aurinkoinvertterin tehoa — ei sammuta sitä, vaan rajoittaa vientiä niin että tuotanto vastaa paremmin omaa kulutusta. Samalla muut kuormat saavat vihreän valon: jos EV tarvitsee latausta tai lämpöpumppu tarvitsee boostia, nyt on oikea hetki.

    Negatiiviset hinnat olivat pitkään harvinaisuus. Nyt ne ovat arkipäivää. 25.5.2026 Etelä-Ruotsissa SE4-alueella hinta kävi yli 1 300 SEK/MWh:ssa illalla ja päivällä oltiin -160 SEK/MWh tasolla — sama alue, sama päivä, hintaero lähes 1 500 SEK/MWh. Hollannissa hinta kävi -86 EUR/MWh.

    Suomessa kehitys on ollut dramaattinen. Kirjoittajan opinnäytetyötä varten kerätyn ENTSO-E-datan perusteella nolla- tai negatiivisia tunteja oli vuosina 2020–2021 vain murto-osa — 16 tuntia vuonna 2020 ja 7 tuntia vuonna 2021. Energiakriisin aikana 2022 luku pysyi matalana, 37 tuntia. Sen jälkeen muutos on ollut jyrkkä: nolla- tai negatiivisen hinnan tunteja kertyi vuonna 2023 jo 603, vuonna 2024 peräti 994 — lähes 41 vuorokautta. Vuonna 2025 tammi–syyskuun ajalta kertyi jo 614 tuntia. Kasvu selittyy uusiutuvan tuotannon — erityisesti tuulivoiman — nopealla lisääntymisellä ja siirtoyhteyksien rajoitteilla. Se on sama rakenteellinen ilmiö joka näkyy koko Pohjois-Euroopassa.

    Halpa hinta (p_cheap_price, oletusarvo 0,04 EUR/kWh)

    Tämän kynnyksen alapuolella sähkö on riittävän edullista että kulutuksen siirtäminen tälle hetkelle on perusteltua — vaikka tarve ei olisi akuutti. Lämpöpumppu saa boostata käyttövettä ennakolta, EV saa ladata vaikka akussa olisi vielä puolet jäljellä. Logiikka ei kuitenkaan pakota toimenpidettä: jos capability-flag on pois tai sulake on uhattuna, halpa hinta ei ohita näitä rajoitteita.

    Kallis hinta (p_expensive_price, oletusarvo 0,15 EUR/kWh)

    Tämän kynnyksen yläpuolella ei-kriittistä kulutusta vähennetään. Lämpöpumppu blokataan EVU-signaalilla jos käyttövesi ja huonelämpötila ovat riittävällä tasolla. EV-lataus estetään. Järjestelmä minimoi ostoa — mutta ei leikkaa lämmitystä jos talo uhkaa jäähtyä alle tavoitelämpötilan. Tarve menee talouden edelle.

    Kaikki kolme kynnystä ovat parametreja — arkkitehtuurissa ei ole kovakoodattuja hintarajoja. Kynnykset ovat lähtöarvoja joita säädetään sopimuksen, siirtomaksun ja omien prioriteettien mukaan.

    Sulakkeet — fyysinen rajoite

    Kolmivaiheinen 25 ampeerin liittymä asettaa absoluuttisen rajan. Sulake ei neuvottele. Se ei katso sähkön hintaa. Se laukeaa kun virta ylittää rajan — ja sen jälkeen talo on pimeänä.

    EnergyHubissa vaihevirrat mitataan erikseen jokaiselta vaiheelta. L1, L2 ja L3 voivat olla hyvin erilaiset. Tässä talossa EV lataa L1-vaiheella — ja se näkyy suoraan: kun EV lataa, L1 on 16–24 ampeeria kun L2 ja L3 ovat 1–2 ampeeria.

    Päätöksenteossa käytetään maksimivaihevirran periaatetta: sulakeriski on siinä vaiheessa joka on eniten kuormitettuna. Ei kokonaisvirran keskiarvossa — siinä vaiheessa joka laukeaa ensin.

    Kotitalouksissa käytettävä gG-sulake sallii hetkellisiä ylityksiä — lyhyt virtapiikki ei laukaise sitä välittömästi. Mutta jatkuva ylikuormitus johtaa väistämättä laukaisuun, ja sen ajoitus riippuu ylityksen suuruudesta. EnergyHub ei odota sulakkeen fysiikkaa — se reagoi ennakoivasti.

    Safety Guardian valvoo vaihevirrat 10 sekunnin välein. Kun maksimivaihevirta ylittää 90 % sulakerajasta — 22,5 ampeeria — Guardian laukaisee peak_protection-moodin välittömästi. Paluu normaaliin tapahtuu vasta kun virta laskee alle 75 % — 18,75 ampeeria. Hystereesi estää moodin flippaamisen edestakaisin.

    25.5.2026 tämä tapahtui oikeasti. Klo 14:05 L1-vaihe nousi 23,8 ampeerin. Guardian laukesi. EV-lataus estettiin. Klo 14:20 virta laski 16,2 ampeerin, järjestelmä palasi normaaliin. Koko tapahtuma kestää 15 minuuttia ja se hoitui automaattisesti.

    Miten hinta ja sulake toimivat yhdessä

    Sulake voittaa aina. Tämä ei ole optimointipäätös — se on fyysinen rajoite joka ei jousta.

    Käytännössä tämä tarkoittaa että Priority Resolver tarkistaa sulaketilanteen ennen hintapäätöksiä. Jos peak_protection on aktiivinen, hintaoptimointia ei tehdä lainkaan — riippumatta siitä onko sähkö halpaa tai kallista.

    Normaalitilanteessa hinta ja sulakkeet toimivat yhdessä näin:

    Onko sulake uhattuna?
      Kyllä → peak_protection, pudota kuormat, älä optimoi
      Ei →
        Onko capability sallittu?
          Ei → älä tee toimintoa riippumatta hinnasta
          Kyllä →
            Onko hinta negatiivinen?
              Kyllä → rajoita PV-vientiä, käytä ylijäämä itse
            Onko hinta halpa?
              Kyllä → lataa EV, boostaa lämpöpumppu
            Onko hinta kallis?
              Kyllä → blokkaa lämpöpumppu, estä EV-lataus
            Muuten →
              Onko aurinkoylijäämää?
                Kyllä → ohjaa ylijäämä omaan kulutukseen
                Ei → jätä laitteet normaalitilaan

    Tämä järjestys on tärkeä. Sulake on ensimmäinen tarkistus — ei hinta. Hintaoptimointia tehdään vain kun sulake ei ole uhattuna.

    Aurinkoinvertterin tehonrajoitus negatiivisilla hinnoilla

    Negatiivisilla hinnoilla aurinkoinvertterin tehonrajoitus on yksi keskeisimmistä toimenpiteistä. Tässä Sungrow SG15RT -invertterissä rajoitus tapahtuu Modbus TCP -yhteydellä — rekisterit 5006 ja 5007 — mutta sama periaate toimii muidenkin valmistajien inverttereillä. Fronius, SMA, ABB, Huawei SUN2000 ja GoodWe tukevat kaikki tehonrajoitusta omilla rajapinnoillaan, ja SunSpec-standardia tukevat laitteet käyttävät yhtenäistä rekisterikarttaa.

    Rajoitus ei tarkoita invertterin sammuttamista. Se tarkoittaa että tuotantoa rajoitetaan niin että vienti verkkoon minimoidaan mutta oma kulutus katetaan edelleen aurinkosähköllä.

    25.5.2026 tehdyssä testissä rajoituksen aikana MPPT-jännite nousi 501 voltista 558 volttiin. Tämä osoittaa että invertteri siirsi paneelien toimintapisteen pois maksimitehopisteestä — se tuotti vähemmän tarkoituksella. Rajoitettu teho oli 3,86 kW kun ilman rajoitusta se olisi ollut noin 12 kW. Rajoitettua tuotantopotentiaalia oli noin 8 kilowattia — mutta tuo 8 kilowattia olisi mennyt verkkoon negatiivisella hinnalla. Energia ei kadonnut — sitä ei yksinkertaisesti tuotettu.

    Hintakynnykset ovat parametreja — ei totuuksia

    Yksi tärkeä periaate EnergyHubissa on se että hintakynnykset ovat säädettäviä parametreja. Mikä on ”halpa” riippuu siirtosopimuksesta, laitteen tehontarpeesta ja omasta kulutusprofiilista.

    Jos sähkösopimus muuttuu tai siirtomaksu nousee, p_electricity_transfer-parametri päivitetään — ja kaikki laskenta päivittyy automaattisesti. Jos haluaa olla aggressiivisempi halvan sähkön hyödyntämisessä, p_cheap_price-kynnystä nostetaan. Jos sulake on tiukempi tai haluaa enemmän turvamarginaalia, p_fuse_limit_a-arvoa lasketaan.

    Logiikka ei muutu — parametrit muuttuvat.

    Sulakepriorisointi kolmivaiheisessa liittymässä

    Kolmivaiheinen liittymä tuo mukanaan yhden mielenkiintoisen haasteen: kuormat eivät jakaudu tasaisesti vaiheille. EV lataa yhdellä vaiheella, lämpöpumppu käyttää kolmea, kiuas yhtä tai kolmea.

    Tässä talossa L1-vaihe on selvästi kuormitetuin — EV, osa valaistuksesta ja osa pistorasioista. Sulakeriski on L1:ssä, ei kokonaisvirrassa. EnergyHub valvoo jokaista vaihetta erikseen ja reagoi siihen vaiheeseen joka on lähimpänä rajaa.

    Tulevaisuudessa tähän liittyy myös tehomaksu. Jos siirtotariffeihin tulee tehomaksukomponentti — kuten monissa maissa on jo käytössä — maksimivirran minimointi muuttuu suoraan taloudelliseksi hyödyksi eikä pelkästään sulakesuojaukseksi.


    Seuraavaksi: Osa 8 — MQTT. Miten viestintäkerros rakentuu, miksi retain-viestit ovat tärkeitä ja miten MQTT-rakenne tukee koko järjestelmän vikasietoisuutta.

    Tekninen toteutus: Nordpool-integraatio, hintaparametrit ja sulakevalvonta on kuvattu ohjeessa HA:n valmistelu integraatiokerrokseksi.

  • Case: oma talo — Osa 6: HA:n rooli ja entiteettimalli

    Tämä on kuudes osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi prioriteetit, tilakoneet ja fallback-logiikka — miten järjestelmä päättää kuka saa sähköä ja mitä tapahtuu kun yhteys katkeaa. Tässä osassa katsotaan miten data organisoidaan Home Assistantissa niin että se pysyy hallittavana kun järjestelmä kasvaa.


    Energiajärjestelmässä on paljon entiteettejä. Modbus-rekisterit, hintatiedot, vaihevirrat, lämpöpumpun anturit, capability-flagit, parametrit, ohjaussignaalit — kaikki asuvat Home Assistantissa ja kaikki tarvitsevat selkeän paikan.

    Ilman rakennetta tämä kertyy nopeasti kasaksi entiteettejä joiden nimet eivät kerro mitään, joiden yksiköt vaihtelevat integraatiosta toiseen ja joita ei kukaan enää muista mistä tulevat. EnergyHubissa rakenne on tehty etukäteen — ei jälkikäteen siivoamalla.

    Neljä roolia, neljä etuliitettä

    Jokainen EnergyHub-entiteetti alkaa etuliitteellä joka kertoo sen roolin järjestelmässä. Tämä ei ole pelkkä nimeämiskäytäntö — se on tietomalli joka tekee näkyväksi mitä kukin entiteetti tekee ja kuka saa käyttää sitä.

    m_ — mittaukset

    Kanoniset, normalisoidut arvot joita kaikki muut järjestelmän osat käyttävät. sensor.m_grid_power_w, sensor.m_pv_power_w, sensor.m_spot_price_eur_kwh. Nämä ovat template-sensoreita jotka normalisoivat raakadatan yhtenäiseen muotoon — oikea yksikkö, oikea etumerkki, oikea skaalaus.

    Node-RED käyttää abstrahoitua järjestelmämallia, ei integraatiokohtaista raakadataa. Se ei tiedä tuleeko tieto Nordpool-integraatiosta, P1-mittarilta vai Modbus-rekisteristä — eikä sen tarvitse tietää. Se lukee m_buy_price_eur_kwh ja saa aina oikean arvon.

    p_ — parametrit

    Kaikki säädettävät kynnysarvot ja raja-arvot. input_number.p_fuse_limit_a, input_number.p_cheap_price, input_number.p_solar_excess_w. Nämä eivät ole hardkoodattuja lukuja automaatioissa tai Node-RED-funktioissa — ne ovat HA:n käyttöliittymässä säädettäviä liukusäätimiä ja syöttökenttiä.

    Kun sulakerajaa pitää muuttaa — esimerkiksi liittymäkoko kasvaa 25:stä 35 ampeerin — muutos tehdään yhteen paikkaan. Node-RED lukee arvon MQTT-telemetrian kautta ja käyttää sitä automaattisesti.

    c_ — capabilityt ja tilaohjaukset

    Capability-flagit ja tilaohjaukset. input_boolean.c_ev_charge_allowed, input_select.c_hp_mode. Kuten aiemmissa osissa selitettiin, c_-etuliite kattaa sekä käyttöoikeudet (c_ev_charge_allowed — saako EV ladata) että tilaohjaukset (c_hp_mode — missä tilassa lämpöpumppu on). Nimestä näkee kumpi on kyseessä: _allowed-päätteiset ovat käyttöoikeuksia, muut ovat tilaohjausta. Nämä eivät ole eksplisiittisiä komentoja — komennot tulevat Node-REDiltä MQTT:n kautta.

    d_ — diagnostiikka

    Ihmisluettavat selitykset järjestelmän päätöksistä. input_text.d_ev_last_reason, input_text.d_hp_last_reason. Nämä eivät ohjaa mitään — ne kertovat miksi viimeisin päätös tehtiin. ”EV estetty: peak_protection, L1=23.8A”. Diagnostiikkaentiteetit tekevät näkymättömästä logiikasta näkyvää ilman että tarvitsee avata Node-REDiä.

    sys_ — järjestelmätila

    Globaalit tilaentiteetit. input_boolean.sys_safety_trip, input_boolean.sys_optimization_enabled, input_select.sys_operating_mode. Nämä ovat koko järjestelmän tila-arvoja joita useat komponentit lukevat.

    Pakettipohja — yksi tiedosto per vastuu

    Entiteetit eivät asu yhdessä suuressa tiedostossa. Ne on jaettu paketteihin loogisten vastuurajojen mukaan:

    00_core.yaml          p_-parametrit, sys_-boolean-arvot
    00_core_modes.yaml    sys_operating_mode, c_-flagit ja moodit
    10_integrations.yaml  Modbus, Nordpool, P1-mittari — raakadata
    20_measurements.yaml  m_-sensorit — normalisoitu data
    30_load_ev.yaml       EV-latauksen tilakone, d_ev_last_reason
    31_load_heatpump.yaml HP EVU/Boost, d_hp_last_reason
    40_optimization.yaml  MQTT-rajapinta Node-REDiin
    50_reporting.yaml     Energiamittarit, utility_meter

    Rakenne muistuttaa modulaarista ohjelmistoarkkitehtuuria: core, integrations, measurements, optimization, reporting. Jokainen kerros on toisistaan riippumaton — 20_measurements.yaml voi muuttua ilman että 40_optimization.yaml:iin tarvitsee koskea.

    Kun EV-latauksen logiikkaan tarvitaan muutos, avataan 30_load_ev.yaml. Kun Modbus-rekisteriin tulee korjaus, avataan 10_integrations.yaml. Tiedoston nimi kertoo suoraan mitä se sisältää.

    Pieni mutta tärkeä täsmennys raportoinnista: 50_reporting.yaml:n utility_meter on operatiivinen raportointikerros — se laskee kuinka paljon sähköä on ostettu tai myyty päivässä, kuukaudessa, vuodessa. Se ei ole sama asia kuin observability. InfluxDB ja Grafana ovat observability-kerros — ne tallentavat aikasarjadatan ja tekevät jälkikäteisanalyysin mahdolliseksi. Molemmat ovat tarpeen, mutta eri tarkoituksiin.

    Kanoninen mittauskerros käytännössä

    20_measurements.yaml on koko järjestelmän tärkein tiedosto vaikka se ei ohjaa mitään. Se on se kerros joka tekee raakadatasta luotettavia mittauksia.

    Raakadata ei ole sama asia kuin luotettava mittaus. P1-mittarin arvo voi olla positiivinen tai negatiivinen riippuen integraatiosta. Nordpool voi palauttaa arvon senttiä tai euroa. Modbus-rekisteri voi palauttaa unknown jos yhteys on poikki. Kanoninen kerros ratkaisee nämä kaikki yhdessä paikassa.

    Esimerkki ostosähkön hintalaskennasta:

    yaml

    - name: "m_buy_price_eur_kwh"
      state: >
        {% set spot = state_attr('sensor.nordpool_kwh_fi_eur_3_00_0',
           'current_price') | float(0) %}
        {% set marginaali = states('input_number.p_electricity_margin')
           | float(0.004) %}
        {% set siirto = states('input_number.p_electricity_transfer')
           | float(0.048) %}
        {{ ((spot * 1.255) + marginaali + siirto) | round(4) }}

    Jos Nordpool-integraation rakenne muuttuu tai siirrytään toiseen hintalähteeseen, korjaus tehdään tähän yhteen templateen. Node-RED ei tarvitse muutosta.

    Signaalin etumerkki — yksi konventio kaikkialle

    m_grid_power_w > 0 tarkoittaa että ostetaan verkosta. m_grid_power_w < 0 tarkoittaa että myydään verkkoon. Sama konventio kaikkialla — ei erikoistapauksia, ei poikkeuksia.

    Tämä tuntuu pieneltä asialta mutta se on yksi niistä päätöksistä joka pitää tehdä kerran ja pitää kiinni aina. Jos jossain kohtaa arvo onkin toisin päin, koko optimointilogiikka tekee vääriä päätöksiä — ja vika on vaikea löytää koska jokainen yksittäinen arvo näyttää oikealta.

    float(0) — hiljainen ongelma ja eri fallback-politiikat

    Template-sensoreissa on yksi yleinen kompromissi: | float(0) muuntaa unknown– ja unavailable-tilat hiljaisesti nollaksi.

    Nolla ei aina tarkoita nollaa. Joskus se tarkoittaa että data puuttuu.

    Kriittinen havainto on se että fallback-oletus riippuu mittauksen kriittisyydestä — kaikilla mittauksilla ei ole sama turvallinen oletusarvo.

    Jos Modbus-yhteys katkeaa ja Sungrow-invertteri menee unavailable-tilaan, m_pv_power_w palauttaa 0. Node-RED tulkitsee sen niin että aurinko ei tuota. Tämä on turvallinen oletus — parempi olettaa nollatuotanto kuin tehdä päätöksiä datalla jonka alkuperä on tuntematon.

    Mutta jos P1-mittari menee unavailable-tilaan ja m_grid_power_w palauttaa 0, tilanne on eri. Node-RED tulkitsee sen niin että verkosta ei osteta eikä myydä. Se voi johtaa väärään päätökseen — esimerkiksi EV-latauksen sallimiseen tilanteessa jossa verkkoteho on oikeasti korkea ja sulake uhkaa laueta.

    PV missing → 0 on turvallinen. Grid missing → 0 ei välttämättä ole turvallinen.

    Kriittisille arvoille kannattaa lisätä diagnostiikkasensori joka havaitsee unavailable-tilan erikseen ja kirjaa sen — ja fallback-politiikka pitää valita mittauksen roolin mukaan. Tämä ei ole vielä EnergyHubissa täysin toteutettu — se on yksi niistä asioista jotka rakentuvat käytön myötä.

    Yksikkö nimessä — ei arvata

    Entiteetin nimi sisältää aina yksikön kun se ei ole itsestään selvä:

    sensor.m_grid_power_w — watteina, ei kilowatteina
    sensor.m_phase_l1_a — ampeereina
    sensor.m_spot_price_eur_kwh — euroina per kilowattitunti, ei senttiä
    sensor.m_hp_tap_water_top_c — celsiusasteina

    Kun Node-RED-funktiossa lukee s.gridPower_W, tietää heti että se on watteina. Ei tarvitse avata integraatiodokumentaatiota tarkistamaan.

    Mitä HA ei sisällä

    HA:n entiteettimalli on tarkoituksella rajattu. Siellä ei ole:

    • Optimointilogiikkaa — se on Node-REDissä
    • Päätöksiä siitä milloin ladata tai lämmittää — ne tulevat Node-REDiltä komennolla
    • Historiadataa aikasarjoina — se on InfluxDB:ssä

    HA on mittaus- ja ohjausrajapinta. Se tekee tehtävänsä hyvin kun se pysyy siinä roolissa.

    Entiteettimalli kasvaa hallitusti

    Kun järjestelmään lisätään uusi laite — olkoon se akku, latauspiste tai uusi lämpöpumppu kuten Nibe, Mitsubishi tai Daikin — se liitetään samaan malliin. Uudet Modbus-rekisterit tulevat 10_integrations.yaml:iin. Normalisoidut mittaukset tulevat 20_measurements.yaml:iin m_-etuliitteellä. Ohjauslogiikka saa oman paketin 3x_-numeroväliin. MQTT-rajapinta laajenee 40_optimization.yaml:ssa.

    Rakenne ei muutu — se laajenee.

    Tämä on se hetki jolloin etukäteen tehty arkkitehtuuripäätös maksaa itsensä takaisin. Uuden laitteen lisääminen on ennakoitavaa työtä, ei arvausta siitä mihin se kuuluu. Harrasteprojekti muuttuu ylläpidettäväksi järjestelmäksi.


    Seuraavaksi: Osa 7 — Hinta ja sulakkeet. Miten spot-hintadata kulkee järjestelmään, miten se yhdistetään sulakerajoihin ja miten nämä kaksi rajoitetta toimivat yhdessä päätöksenteossa.

    Tekninen toteutus: Entiteettimalli, nimeämiskäytäntö ja pakettipohja on kuvattu tarkemmin ohjeessa HA:n valmistelu integraatiokerrokseksi.

  • Case: oma talo — Osa 5: Prioriteetit, tilakoneet ja fallback

    Tämä on viides osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi kerrosmalli ja vastuunjako — miten neljä kerrosta kommunikoivat keskenään MQTT:n kautta. Tässä osassa päästään siihen kysymykseen joka nousee aina kun järjestelmä kasvaa riittävän monimutkaiseksi: kuka saa sähköä kun kaikki haluavat sitä yhtä aikaa?


    Tiistai-iltapäivä. Aurinko paistaa, invertterin teho on 12 kW. Sähköauto on kytkettynä ja haluaa ladata. Lämpöpumppu harkitsee käyttöveden lämmittämistä. Kiuas on ajastettu käynnistymään tunnin kuluttua. L1-vaihe on 23 ampeeria.

    Neljä tavoitetta, yksi liittymä, 25 ampeerin sulake.

    Järjestelmä tarvitsee selkeän vastauksen siihen kenen pyyntö menee edelle. Ei kompromissia — prioriteetti.

    Tilakone — yksi moodi kerrallaan

    EnergyHubissa koko järjestelmä on aina yhdessä tilassa. Operating mode ei ole pelkkä informaatioarvo — se on prioriteettikerros joka määrittää miten kaikki päätökset tehdään.

    Tilat järjestyksessä prioriteetin mukaan:

    emergency          ← korkein prioriteetti — turvatila
    peak_protection    ← sulake uhkaa — pudota kuormat
    manual_override    ← ihminen päättää — ei automaatiota
    vacation           ← talo tyhjillään — minimikäyttö
    guest              ← mukavuus prioriteetiksi
    cheap_energy       ← halpa sähkö — lataa kaikki
    solar_maximize     ← aurinko ylituottaa — käytä itse
    normal             ← oletustila — hinta + aurinko + sulakkeet

    Fallback ei ole varsinainen operating mode — se on järjestelmän terveystila. Se aktivoituu automaattisesti kun data on vanhentunutta tai yhteys on katkennut. Siinä ei kilpailla muiden moodien kanssa: jos järjestelmä ei tiedä missä tilassa se on, se siirtyy konservatiivisiin oletuksiin riippumatta siitä mitä operating mode sanoo.

    Tärkeää on ymmärtää mitä prioriteettijärjestys tarkoittaa käytännössä: kun moodi on peak_protection, Node-RED ei enää kysy onko hinta halpa tai tuottaako aurinko. Se pudottaa kuormat prioriteettijärjestyksessä riippumatta muista olosuhteista. Moodi ohittaa optimoinnin.

    Miten moodi muuttuu — ja kuka saa muuttaa

    Tilamuutos voi tulla kolmesta suunnasta — mutta ei kaikista suunnista kaikkiin tiloihin.

    Safety Guardian muuttaa moodin välittömästi kun vaihevirta ylittää 90 % sulakerajasta. Tämä tapahtuu 10 sekunnin syklillä — ei odoteta seuraavaa minuuttipäätöstä. Guardian voi asettaa peak_protection-moodin, mutta se ei voi itse poistaa sitä — paluu normal-moodiin tapahtuu kun vaihevirta laskee alle 75 % rajan. Hystereesi estää sen että moodi flippaa edestakaisin pienten vaihteluiden mukana.

    emergency-moodista poistuminen vaatii manuaalisen toimenpiteen — käyttäjän tai huolto-operaation. Se ei nollaudu automaattisesti.

    Node-RED Decision Engine toimii optimointi- ja päätöksentekokerroksena. Se voi pyytää moodinvaihtoa kun se havaitsee tilanteen muuttuvan — cheap_energy kun hinta putoaa alle kynnyksen, solar_maximize kun aurinkoylijäämä kasvaa riittäväksi. Se ei voi kuitenkaan ohittaa emergency– tai manual_override-moodia.

    Käyttäjä voi vaihtaa moodin suoraan HA:n käyttöliittymästä. manual_override tarkoittaa että järjestelmä ei tee omia päätöksiä — ihminen on ottanut ohjat. Tämä on ainoa tila johon vain käyttäjä pääsee.

    Kaikki tilamuutokset kirjataan. InfluxDB:stä näkee jälkikäteen milloin moodi muuttui, mihin suuntaan ja mikä sen aiheutti.

    Priority Resolver — päätöslogiikka järjestyksessä

    Node-RED:n Priority Resolver käy tilanteet läpi kiinteässä järjestyksessä. Ensimmäinen osuma ratkaisee — loput haarasta jätetään käymättä läpi.

    1. Emergency ja safety trip

    Jos sys_safety_trip on true tai moodi on emergency, kaikki ohjattavat kuormat sammutetaan välittömästi. EV-lataus estetään, lämpöpumppu blokataan EVU-signaalilla, lisävastus sammutetaan, kiuas estetään. PV-rajoitusta ei aseteta — aurinko saa tuottaa vapaasti koska se pienentää verkkokuormaa. Tässä tilanteessa ei optimoida. Turvallisuus menee edelle.

    2. Peak protection

    Vaihevirta on liian korkea tai Safety Guardian on laukaissut moodin. Kuormat pudotetaan prioriteettijärjestyksessä:

    • EV-lataus estetään ensimmäisenä — suurin yksittäinen kuorma, 3,6 kW
    • Kiuas estetään — toinen suuri kuorma
    • Lisävastus estetään
    • Lämpöpumppu: jos kompressori pyörii yli 60 % teholla, blokataan EVU-signaalilla. Jos alle, annetaan jatkaa normaalisti.

    PV:tä ei rajoiteta — aurinkoinvertteri saa tuottaa vapaasti koska se pienentää verkon kuormaa.

    3. Manual override

    Moodi on manual_override — järjestelmä ei tee mitään. Se ei muuta mitään tilaa, ei lähetä komentoja. Ihminen päättää.

    4. Vacation

    Talo on tyhjillään. EV-lataus estetään. Kiuas estetään. Lämpöpumppu: jos käyttövesi on laskenut alle minimilämpötilan tai huonelämpötila on laskenut alle asetetun tavoitelämpötilan, annetaan lämmittää normaalisti. Muuten blokataan.

    5. Normaali optimointi

    Kaikki muut moodit — normal, solar_maximize, cheap_energy, guest — päätyvät tähän haaraan. Tässä otetaan huomioon hinta, aurinkotuotanto ja sulakerajat yhdessä.

    Normaali optimointi — kolme muuttujaa

    Normaalissa optimoinnissa kolme tekijää vaikuttaa jokaiseen päätökseen:

    Hinta — spot-hinta verrattuna kahteen kynnysarvoon. Negatiivinen hinta tarkoittaa että sähköstä maksetaan verkkoon myymisestä — tällöin kannattaa rajoittaa vientiä ja käyttää kaikki itse. Halpa hinta tarkoittaa että lataaminen ja lämmittäminen kannattaa. Kallis hinta tarkoittaa päinvastoin.

    Aurinkotuotanto — ylijäämäaurinko on ensisijainen energianlähde omaan käyttöön. Jos aurinko tuottaa enemmän kuin talo kuluttaa, ylijäämä ohjataan ensin EV-lataukseen, sitten lämpöpumpun boostiin. Verkkoon myynti on viimeinen vaihtoehto — ja negatiivisilla hinnoilla jopa haitallinen.

    Sulakkeet — vaihevirrat rajoittavat kaiken muun. Optimointilogiikka ei koskaan tee päätöstä joka johtaisi sulakeylitykseen. Tämä ei ole pelkästään Safety Guardianin vastuulla — se on myös optimointilogiikan sisäänrakennettu rajoite.

    Capability — lupa ennen päätöstä

    Ennen kuin Priority Resolver tekee yhtään päätöstä, se tarkistaa capability-flagit. c_ev_charge_allowed: false tarkoittaa että EV-latausta ei sallita riippumatta hinnasta tai aurinkotilanteesta.

    Capability on käyttöoikeus — vastaus kysymykseen ”saako tämä laite toimia”. Se ei kerro tekeekö järjestelmä niin. Komento on se päätös joka lähetetään laitteelle.

    Tämä erottelu on tärkeä: käyttäjä voi asettaa capability-flagin pois päältä — ”en halua ladata tänä yönä” — ilman että logiikkaan tarvitsee koskea. Järjestelmä kunnioittaa rajoitteen.

    Shadow/live-mekanismi on eri abstraktiotaso. Se ei vastaa kysymykseen ”saako laite toimia” vaan ”saako EnergyHub ohjata laitetta”. Capability on käyttöoikeus laitteelle, shadow/live on execution control — kuka omistaa ohjauksen. Nämä kaksi toimivat yhdessä mutta eri tasoilla.

    Fallback — mitä tapahtuu kun yhteys katkeaa

    Fallback-logiikka on se osa järjestelmästä josta harvoin puhutaan mutta joka on yhtä tärkeä kuin optimointilogiikka.

    Kun järjestelmä ei tiedä mikä on tosi — data on vanhentunutta, yhteys on poikki, mittaukset eivät päivity — se ei arvaa. Se siirtyy konservatiivisiin oletuksiin: EV-lataus estetään, lämpöpumppu jatkaa normaalisti ilman boostia, ei aggressiivista optimointia. Parempi jättää käyttämättä optimointimahdollisuus kuin tehdä väärä päätös epävarman datan perusteella — erityisesti kun EV-lataus on suurin yksittäinen kuorma ja sulakeylilyönti on mahdollinen.

    EnergyHubissa fallback toimii kerroksittain.

    Jos Node-RED kaatuu, HA jatkaa mittaamista. Laitteet jäävät viimeiseen tunnettuun tilaan — Shelly-releet eivät liiku, invertteri jatkaa viimeisellä rajoitusasetuksella. Ei optimaalista, mutta turvallista.

    Jos HA kaatuu, Node-RED lukee MQTT:n retain-viesteistä viimeisimmän tunnetun tilan. Se ei saa uutta dataa, joten se siirtyy konservatiivisiin oletuksiin.

    Jos MQTT-broker kaatuu, kaikki kommunikaatio lakkaa. Laitteet jäävät tilaan johon ne viimeksi ohjattiin.

    Jokaisessa tapauksessa fyysiset suojaukset — sulakkeet, invertterin suojat, lämpöpumpun termostaatit — toimivat edelleen. Ne eivät riipu ohjelmistosta.

    Viimeiseen tunnettuun tilaan jääminen on turvallinen ratkaisu lyhyellä aikavälillä. Pitkällä aikavälillä olosuhteet muuttuvat — aurinkotuotanto laskee, ulkolämpötila vaihtelee, sähkön hinta nousee. Järjestelmä joka on pysähtynyt ei pysty reagoimaan näihin muutoksiin. EV-rele voi jäädä kiinni kun aurinko on laskenut ja sähkö on kallista — tai auki kun aurinko olisi riittänyt lataukseen. Tila joka oli järkevä kaatumishetkellä ei välttämättä ole järkevä kaksi tuntia myöhemmin.

    Tästä syystä pitkä kaatuminen ei ole neutraali tila — se on tila jossa optimointi on poissa käytöstä ja laitteet toimivat viimeisen komennon mukaan riippumatta siitä onko se enää perusteltua.

    Käytännön ratkaisu on watchdog-logiikka: jos järjestelmä havaitsee että se ei ole saanut uutta dataa tietyn ajan kuluessa, se asettaa laitteet eksplisiittisesti konservatiiviseen tilaan — EV-rele auki, invertteri täydelle teholle, lämpöpumppu normaalille ilman boostia. Tämä on parempi kuin se että laitteet jäävät tuntemattomaan tilaan ilman valvontaa. Watchdog on yksi niistä asioista jotka rakennetaan kun järjestelmä on muuten toimiva — mutta sen puuttuminen huomataan vasta kun kaatuminen kestää odotettua kauemmin.

    Käytännön esimerkki: peak protection laukeaa

    25.5.2026 klo 14:00. Aurinkoa riittää, sähkön hinta on 2,3 senttiä per kilowattitunti. EV-lataus on sallittu ja lämpöpumppu toimii normaalisti.

    Klo 14:05. L1-vaihe nousee 23,8 ampeerin. Safety Guardian havaitsee ylityksen 10 sekunnin syklillä — 90 % 25 ampeerin rajasta on 22,5 ampeeria, raja ylittyy selvästi. Guardian julkaisee energyhub/command/mode: peak_protection. HA kirjaa tilamuutoksen.

    Node-RED lukee seuraavalla minuuttisyklillä tilanteen: moodi on peak_protection. Priority Resolver menee suoraan peak-haaraan riippumatta hinnasta tai aurinkotuotannosta. Päätös: EV off, kiuas off, HP normal — kompressori alle 60 %.

    Klo 14:20. Suuri kulutus loppuu. L1-vaihe laskee 16,2 ampeerin. Safety Guardian havaitsee että ollaan alle 75 % rajan — 18,75 ampeeria. Guardian julkaisee energyhub/command/mode: normal. Järjestelmä palaa optimointimoodiin.

    Koko tapahtuma kestää 15 minuuttia. Päätökset on kirjattu observability-topiciin syineen. InfluxDB näyttää vaihevirran kehityksen kaavioina.

    Miksi tilakone eikä ehtojen lista

    Vaihtoehto tilakoneelle olisi rakentaa optimointilogiikka pitkänä ehtolistana: ”jos hinta < X ja virta < Y ja aurinko > Z, tee A, muuten jos…”

    Tämä toimii pienessä järjestelmässä. Mutta kun ehtoja on kymmeniä ja ne alkavat olla ristiriidassa keskenään, kukaan ei enää tiedä mitä järjestelmä tekee missäkin tilanteessa. Vika löytyy vasta kun se on jo tapahtunut — väärässä tilanteessa, väärällä hetkellä.

    Tilakone pakottaa ajattelemaan etukäteen. Jokainen moodi on eksplisiittinen. Tilasiirtymät ovat määriteltyjä — ja tärkeää on myös se kuka saa tehdä minkäkin siirtymän. Mikä tahansa tilanne voidaan kysyä: ”missä moodissa järjestelmä on ja mitä se tässä moodissa tekee?” — ja saada selkeä vastaus.

    Se on kotiautomaatiossa harvinainen luksus.


    Seuraavaksi: Osa 6 — HA:n rooli ja entiteettimalli. Miten mittaukset, parametrit ja ohjaussignaalit järjestetään niin että ne pysyvät hallittavina kun järjestelmä kasvaa.

    Tekninen toteutus: Priority Resolver -koodi ja operating mode -rakenne on kuvattu ohjeessa HA:n valmistelu integraatiokerrokseksi.

  • Kun hinta ei enää riitä – miksi tulevaisuuden EnergyHub tarvitsee myös verkkotietoa

    Edellisessä artikkelissa kävin läpi miksi kotitalouksien energian optimointi ei skaalaudu. Syy oli arkkitehtuurinen: ohjaus tapahtuu väärällä tasolla, kustannus on väärässä paikassa ja järjestelmät eivät kommunikoi keskenään.

    Mutta on vielä syvempi ongelma.

    Vaikka kaikki kodit alkaisivat optimoida kulutustaan pörssisähkön hinnan perusteella, se ei vielä riittäisi. Koska hinta on vain yksi signaali sähköjärjestelmästä. Se kertoo markkinatilanteen — mutta ei verkon fyysistä tilaa.

    Ja juuri siitä on kyse seuraavassa vaiheessa.

    Verkkoa ei rakenneta nopeasti

    Suomen sähköjärjestelmä perustuu laajaan suurjänniteverkkoon, joka yhdistää voimalaitokset, kantaverkon, alueverkot ja jakeluverkot toisiinsa. Merkittävä osa alueellisesta sähkönsiirrosta tapahtuu 110 kilovoltin verkoissa, jotka muodostavat tärkeän linkin kantaverkon ja paikallisten jakeluverkkojen välille.

    Kun alue kasvaa — uusia koteja, latauspaikkoja, teollisuutta — myös verkon kapasiteettia pitää kasvattaa. Ja tässä tulee ensimmäinen ongelma.

    110 kilovoltin sähköjohdon rakentaminen ei tapahdu nopeasti.

    Ennen kuin ensimmäinen pylväs pystytään, tarvitaan Energiaviraston hankelupa. Luvantarpeesta säädetään sähkömarkkinalaissa: vähintään 110 kilovoltin johto vaatii luvan ennen rakentamista. Hankelupa on voimassa viisi vuotta lainvoimaiseksi tulosta.

    Mutta lupakäsittely on vasta alkua. Sen jälkeen tulevat maankäyttö- ja lunastusmenettelyt, ympäristöselvitykset, mahdolliset valitukset ja itse rakentaminen.

    Kokonaisaikataulu voi suurissa hankkeissa venyä useisiin vuosiin. Suunnittelu, ympäristöselvitykset, luvitus, maanomistajaneuvottelut ja rakentaminen muodostavat yhdessä pitkän prosessin.

    Pääkaupunkiseudulla Fingridin Helsingin kaapeli -hanke käynnistyi vuonna 2020 ja uusi 400 kilovoltin kaapeliyhteys valmistuu vuonna 2026. Jo tämä yksittäinen hanke havainnollistaa, että verkkoinvestointien aikajänne lasketaan vuosissa, ei kuukausissa.

    Hyvinkään ja Riihimäen seudun verkon vahvistaminen on toinen esimerkki samasta ilmiöstä. Siirtokyvyn kasvattaminen vie aikaa ja rahaa — eikä se tapahdu silloin kun kulutus kasvaa, vaan vuosia sen jälkeen.

    Pullonkaula ei näy hinnassa — ennen kuin on liian myöhäistä

    Tässä on keskeinen ongelma.

    Nordpool-hinta kertoo koko Suomen markkinatilanteen. Se ei kerro, onko Espoon pohjoinen syöttöasema lähellä kapasiteettirajaansa. Se ei kerro, onko Hyvinkään ja Riihimäen välinen 110 kilovoltin yhteys kuormittunut kovana pakkaspäivänä.

    Hinta voi heijastaa laajempaa alueellista tai valtakunnallista niukkuutta, mutta paikallinen jakeluverkon pullonkaula ei välttämättä näy Suomen tarjousalueen hinnassa lainkaan.

    Tämä tarkoittaa, että hintaohjaukseen perustuva kotiautomaatio optimoi väärää asiaa. Se siirtää kulutusta halvoille tunneille — mutta ei välttämättä verkon kannalta oikeisiin hetkiin.

    Hintaohjaus voi jopa kasvattaa paikallista huipputehoa

    Hintaohjaus ei automaattisesti vähennä verkkokuormitusta.

    Jos suuri määrä koteja reagoi samaan halpaan tuntiin samalla tavalla, sähköautojen lataus, käyttövesivaraajat ja lämpöpumput voivat käynnistyä samanaikaisesti.

    Yksittäisen kotitalouden näkökulmasta optimointi toimii. Sähkölasku pienenee ja kulutusta siirtyy halvemmille tunneille.

    Paikallisen verkon näkökulmasta lopputulos voi kuitenkin olla toinen. Kuorma kasautuu samoille tunneille, jolloin huipputeho kasvaa.

    Tämä on yksi syy siihen, miksi tulevaisuudessa tarvitaan myös verkkotietoisia ohjaussignaaleja.

    FinFlex — ensimmäinen askel oikeaan suuntaan

    Huhtikuussa 2025 avattiin jotain uutta.

    Fingrid ja Helen Sähköverkko avasivat Suomen ensimmäisen yhteisen siirtojenhallinnan markkinan — FinFlexin. Kyseessä on TSO-DSO-joustomarkkinan pilotti, jossa sekä kantaverkon että jakeluverkon pullonkauloja hallitaan yhden markkinapaikan kautta. Kauppapaikkana toimii NODES-alusta.

    Tämä on merkittävä askel. Ensimmäistä kertaa Suomessa verkon fyysinen kapasiteettitilanne ja markkinamekanismi yhdistyvät samaan paikkaan.

    Käytännössä tämä tarkoittaa, että joustoresurssit — sähköautot, lämpöpumput, akut ja teollisuuskuormat — voivat tarjota kapasiteettia silloin kun verkko sitä tarvitsee. Ei vain silloin kun hinta on korkea.

    FinFlex on vielä pilotti. Se ei vielä koske yksittäisiä kotitalouksia suoraan.

    FinFlex ei myöskään tarkoita, että yksittäinen lämpöpumppu tai sähköauto saisi suoraan verkkoyhtiöltä ohjauskäskyn. Se osoittaa kuitenkin suunnan: verkon fyysinen kapasiteettitilanne voidaan muuttaa markkinasignaaliksi, jota joustoresurssit voivat hyödyntää.

    Mitä tämä tarkoittaa kotiautomaatiolle

    Tähän asti kodin energianhallinta on perustunut yksinkertaiseen signaaliin: Nordpool-hintaan.

    Se on hyvä lähtökohta. Mutta se ei riitä, kun sähköjärjestelmä monimutkaistuu.

    Tulevaisuuden kodin energiajärjestelmä tarvitsee useampia signaaleja.

    Hinta kertoo markkinatilanteen. Se on edelleen tärkein signaali yksittäiselle kotitaloudelle.

    Aurinkoylijäämä kertoo oman tuotannon tilanteen.

    Sulakekuorma kertoo oman liittymän tilan.

    DSO-signaali voisi tulevaisuudessa kertoa jakeluverkkoyhtiön kapasiteettitilanteesta. Onko alueellinen verkko kuormittunut? Pitäisikö kulutusta siirtää? Tällaisia signaaleja ei vielä ole kotitalouksille yleisesti tarjolla.

    Tehomaksu on nousemassa yhä tärkeämmäksi osaksi sähkönsiirtoa. Esillä olleissa malleissa pienasiakkaiden tehomaksut on usein sidottu noin 8 kilowatin tehorajaan, jonka ylittävästä huipputehosta peritään erillinen maksu. Tämä muuttaa optimointiongelman luonnetta — energian hinnan lisäksi myös hetkellinen teho alkaa vaikuttaa kustannuksiin.

    V2G-signaali tulevaisuudessa: sähköauto ei vain lataa, vaan voi myös tarjota energiaa takaisin sähköjärjestelmälle silloin kun sitä tarvitaan.

    Koti joustavana resurssina — ilman asumismukavuuden heikkenemistä

    Tässä on ydinajatus, joka erottaa tulevan mallin nykyisestä.

    Kotitalous ei ole vain kuluttaja. Se on joustoresurssi.

    Mutta jouston pitää tapahtua ehdoilla, jotka sopivat asumiseen. Ei niin, että verkkoyhtiö sammuttaa lämmityksen pakkasella. Vaan niin, että kodin energiajärjestelmä osaa itse päättää, milloin jousto on mahdollista ilman vaikutusta asumismukavuuteen.

    Lämpöpumppu voi olla EVU-tilassa tunnin ajan, jos varaajassa on riittävästi lämpöenergiaa. Sähköauto voi lykätä latausta puoli tuntia, jos se ehtii silti täyteen ennen lähtöä. Aurinkovoimala voi rajoittaa tuotantoaan negatiivisten hintojen aikana.

    Kaikki nämä ovat pieniä joustoja.

    Mutta miljoona kotia tekemässä niitä koordinoidusti on jo merkittävä resurssi sähköjärjestelmälle.

    Grid-Aware Control — seuraava kerros EnergyHubiin

    HEOMF-viitekehyksessä tämä voidaan nähdä uutena ulottuvuutena: Grid-Aware Control.

    Nykyinen EnergyHub optimoi esimerkiksi hinnan, aurinkotuotannon, sulakekuorman ja käyttäjän tavoitteiden perusteella.

    Mutta seuraava taso lisää mukaan verkon tilan ja tehoperusteiset kustannukset.

    Tämä ei tarkoita, että kodin järjestelmä pitää rakentaa uudelleen. Se tarkoittaa, että arkkitehtuuriin lisätään uusi syöte — esimerkiksi DSO-signaali tai tehomaksulaskuri — ja päätöslogiikka ottaa sen huomioon.

    Arkkitehtuuri ensin. Optimointi seuraa.

    Miksi tämä on tärkeää nyt

    Sähköverkko rakentuu hitaasti. Luvitusprosessit kestävät vuosia. Uusi 110 kilovoltin johto ei synny silloin kun kulutus kasvaa — vaan parhaimmillaankin vuosia myöhemmin.

    Tässä välissä kapasiteettia pitää löytää muualta.

    Ja se muualta on ohjaus.

    Jos kodit, sähköautot ja lämpöpumput osaavat reagoida verkon tilaan — eivät vain hintaan — kapasiteettia vapautuu ilman uutta johtoa.

    FinFlex on ensimmäinen konkreettinen askel siihen, että tällainen koordinointi on mahdollinen.

    Kotiautomaatio ei muutu passiivisesta säästäjästä aktiiviseksi verkon resurssiksi yhdessä yössä. Mutta arkkitehtuuri kannattaa rakentaa niin, että se on mahdollista.

    Sähköjärjestelmän seuraava haaste ei välttämättä ole energian riittävyys. Yhä useammin kyse on siitä, saadaanko energia oikeaan paikkaan oikeaan aikaan ilman että verkkoa joudutaan jatkuvasti vahvistamaan.

    Lopuksi

    Pörssisähkö on tehnyt kotitalouksista aktiivisempia toimijoita sähköjärjestelmässä.

    Se on ollut hyvä alku.

    Mutta tulevaisuuden sähköjärjestelmä tarvitsee enemmän kuin yhden signaalin.

    Kun sähköautot, lämpöpumput, aurinkosähkö ja akustot yleistyvät, pelkkä hintatieto ei enää riitä kuvaamaan koko järjestelmän tilaa.

    Siksi seuraava looginen askel ei välttämättä ole parempi hintaoptimointi.

    Se voi olla verkkotietoinen energianhallinta.

    Järjestelmä, joka ei kysy pelkästään:

    ”Milloin sähkö on halpaa?”

    Vaan myös:

    ”Milloin sähköjärjestelmä tarvitsee minua?”


    Seuraavassa osassa: joustomarkkina käytännössä — mitä aggregaattorit tekevät, mitä V2G tarkoittaa suomalaiselle sähköautoilijalle ja milloin verkkosignaalit voivat alkaa näkyä yksittäisen kodin energianohjauksessa.

    Piditkö artikkelista?

    Seuraa blogia myös Blogit.fi:ssä, niin löydät uudet kirjoitukset helposti.

    Seuraa blogia Blogit.fi:ssä
  • Case: oma talo — Osa 4: Kerrosmalli ja vastuunjako

    Tämä on neljäs osa sarjasta joka dokumentoi EnergyHub-järjestelmän rakentamisen vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi Home Assistantin rooli integraatiokerroksena — mitä se tekee ja mitä se jättää tekemättä. Tässä osassa katsotaan miten kaikki kerrokset toimivat yhdessä ja kuka vastaa mistäkin.


    Kotitalouden energiajärjestelmä on harhaanjohtavan yksinkertainen paperilla. Aurinko tuottaa, lämpöpumppu lämmittää, auto latautuu. Mutta kun kaikki tapahtuu samanaikaisesti — ja samaan aikaan sähkön hinta on negatiivinen, L1-vaihe lähestyy 25 ampeeria ja lämpöpumpun käyttövesi on viilenemässä — tarvitaan selkeä kuva siitä kenen vuoro on päättää.

    EnergyHubissa vastaus on kerrosmalli. Jokainen kerros tietää oman vastuualueensa eikä astu toisen tontille.

    Neljä kerrosta, neljä vastuuta

    ┌─────────────────────────────────────────────┐
    │  HAVAINTOKERROS                             │
    │  InfluxDB + Grafana                         │
    │  Tallentaa kaiken — tekee näkymättömän      │
    │  näkyväksi jälkikäteen                      │
    ├─────────────────────────────────────────────┤
    │  OPTIMOINTIKERROS                           │
    │  Node-RED                                   │
    │  Päättää mitä tehdään — priorisoi,          │
    │  tasapainottaa, optimoi                     │
    ├─────────────────────────────────────────────┤
    │  INTEGRAATIOKERROS                          │
    │  Home Assistant                             │
    │  Lukee, normalisoi, välittää — ylläpitää    │
    │  järjestelmän tilaa                         │
    ├─────────────────────────────────────────────┤
    │  KENTTÄKERROS                               │
    │  Modbus · Shelly · P1-mittari               │
    │  Toimii itsenäisesti — fyysiset suojaukset  │
    │  eivät riipu ylemmistä kerroksista          │
    └─────────────────────────────────────────────┘

    Kerrosten välinen kommunikaatio tapahtuu MQTT:n kautta. Kenttäkerroksen laiteprotokollat — kuten Modbus TCP aurinkoinvertterin ja lämpöpumpun kanssa — jäävät integraatiokerroksen sisälle, eivätkä näy ylöspäin. MQTT-broker, tässä tapauksessa Mosquitto, toimii väylänä jossa jokainen kerros julkaisee omaan suuntaansa ilman suoria kytköksiä toisiin kerroksiin.

    Kenttäkerros — toimii aina

    Kenttäkerroksen laitteet — Sungrow-aurinkoinvertteri, Thermia-maalämpöpumppu, Shelly-releet, HomeWizard P1 -mittari — toimivat omalla logiikallaan riippumatta siitä mitä ylemmissä kerroksissa tapahtuu. Sama pätee laajemmin: Fronius Symo, SMA Sunny Boy, ABB-invertterit, Huawei SUN2000 ja GoodWe toimivat kaikki itsenäisesti — EnergyHub ohjaa niitä, mutta ei ole niiden toiminnan edellytys. Nibe-, Mitsubishi-, Daikin- ja Vaillant-lämpöpumput ovat samassa asemassa kuin Thermia: ne lämmittävät taloa omalla säätölogiikallaan, EnergyHub vain antaa lisäohjeita.

    Kenttäkerroksessa on fyysisesti toimivat turvasuojaukset: aurinkoinvertterin ylijännitesuoja, lämpöpumpun kompressorin suojatermostaatti ja sähkökeskuksen sulakkeet. Nämä toimivat laitehardwaressa — eivät ohjelmiston varassa. EnergyHubin ohjelmallinen kuormanpudotus on lisäsuoja näiden päällä, ei niiden korvike.

    Integraatiokerros — lukee ja välittää

    Home Assistant lukee kenttäkerroksen laitteiden tilat Modbus TCP:n kautta ja normalisoi ne kanonisiksi mittauksiksi. sensor.m_grid_power_w, sensor.m_pv_power_w, sensor.m_spot_price_eur_kwh — nämä ovat yhtenäisiä arvoja joita kaikki muut kerrokset käyttävät.

    HA myös vastaanottaa Node-REDin komennot MQTT:sta ja välittää ne laitteille. Kun Node-RED päättää että lämpöpumppu tarvitsee boostin, se julkaisee energyhub/command/hp/mode: boost — HA kuuntelee, tulkitsee ja ohjaa Shelly Pro 2:n relettä sen mukaisesti.

    HA ylläpitää myös järjestelmän tilaa. Operating mode — normal, peak_protection, vacation, emergency — asuu HA:ssa. Node-RED voi pyytää tilamuutosta, mutta HA kirjaa sen ja julkaisee kaikkien saataville. Näin tila on aina yhdessä paikassa.

    Optimointikerros — päättää

    Node-RED on järjestelmän aivot. Se vastaanottaa kaiken telemetrian MQTT:n kautta, ylläpitää flow-kontekstissa ajantasaisen kuvan tilanteesta ja ajaa päätöslogiikan minuutin välein.

    Päätöslogiikka on eksplisiittistä JavaScript-koodia — ei piilotettuna YAML-automaatioiden ketjuun. Kun Node-RED päättää sallia EV-latauksen, se tekee sen koska spot-hinta on alle kynnysarvon tai aurinkoylijäämä ylittää rajan. Syy on koodissa luettavissa, ei arvailtavissa.

    Safety Guardian on erillinen flow joka reagoi välittömästi — ei minuutin syklillä vaan 10 sekunnin välein saapuviin vaihevirtatietoihin. Kun L1-vaihe lähestyy 25 ampeerin sulakerajaa, Safety Guardian laukaisee peak_protection-moodin välittömästi odottamatta seuraavaa minuuttipäätöstä. Vaihevirtatiedot tulevat HA:n kautta — jos HA käynnistetään uudelleen, reaaliaikainen valvonta palautuu vasta kun HA alkaa julkaista uutta telemetriaa.

    Node-RED kirjaa jokaisen päätöksensä perusteluineen energyhub/observability/decisions-topiciin — ei vain mitä tehtiin, vaan miksi tehtiin. Tämä ei ole ohjausta varten vaan auditointia varten. Jos joku kysyy miksi EV ei latautunut tiistaina klo 14, vastaus löytyy observability-topicista: virta oli liian korkea, moodi oli peak_protection, lataus estettiin tästä syystä.

    Havaintokerros — tekee näkymättömän näkyväksi

    InfluxDB tallentaa kaiken aikasarjadatana — vaihevirrat, spot-hinnat, aurinkotuotannon, lämpöpumpun kompressorin kierrosluvun, päätökset ja niiden syyt. Grafana visualisoi sen.

    Havaintokerros ei ohjaa mitään. Se ei lähetä komentoja eikä muuta tiloja. Mutta se on se kerros joka paljastaa onko järjestelmä oikeasti toiminut kuten piti — ja tekee mahdolliseksi kysyä kysymyksiä datalta jälkikäteen.

    Tämä on HEOMF-kypsyysmallissa keskeinen asia: järjestelmä jota ei voi analysoida jälkikäteen, on järjestelmä jota ei voi parantaa.

    MQTT — kerrosten yhteinen kieli

    MQTT on se liima joka pitää kerrokset yhdessä mutta löyhästi kytkettynä. Kerrosten välinen kommunikaatio tapahtuu MQTT:n kautta — ei suoria funktiokutsuja, ei jaettuja tietorakenteita.

    Topicrakenne noudattaa selkeää logiikkaa:

    energyhub/telemetry/     →  HA julkaisee mittauksia ylöspäin
    energyhub/command/       →  Node-RED antaa komentoja alaspäin
    energyhub/system/        →  järjestelmätilan viestit
    energyhub/observability/ →  päätösloki analyysiin

    Tämä tarkoittaa että Node-RED voidaan käynnistää uudelleen — se lukee MQTT:n retain-viesteistä viimeisimmän tunnetun tilan ja jatkaa siitä. HA voidaan käynnistää uudelleen — Node-RED odottaa uusia telemetriaviestejä ja jatkaa heti kun niitä alkaa tulla. Kummankin voi käynnistää uudelleen ilman että koko järjestelmä menee sekaisin, vaikka lyhyt katkos reaaliaikaisessa päätöksenteossa syntyy.

    Tämä ei ole onnettomuus — se on suunniteltu ominaisuus.

    Vastuunjako käytännössä

    Yksi konkreettinen esimerkki selventää vastuunjaon paremmin kuin mikään kaavio.

    Klo 14:30 aurinko tuottaa 12 kW, spot-hinta on -0,001 EUR/kWh, L1-vaihe on 16 ampeeria ja EV on kytkettynä.

    Kenttäkerros: Sungrow raportoi 12 kW tuotannon Modbus-rekistereistä. P1-mittari raportoi L1=16 A.

    Integraatiokerros: HA lukee arvot, normalisoi ne kanonisiksi sensoreiksi ja julkaisee MQTT:hen: grid=-3695 W (vienti), pv=12000 W, spot=-0.001 EUR/kWh, L1=16.2 A.

    Optimointikerros: Node-RED lukee tilanteen. Hinta on negatiivinen — PV-rajoituslogiikka laskee sopivan rajoitusprosentin omakäytön perusteella ja julkaisee energyhub/command/pv/curtail_pct: 24. EV-lataus sallitaan koska se käyttää ylijäämää eikä nosta vaihevirta yli rajan.

    Integraatiokerros: HA vastaanottaa PV-rajoituskomennon ja kirjoittaa tässä Sungrow-toteutuksessa Modbus-rekisteriin 5007 arvon 240 (24,0 %). Invertterin ohjausrekisterit vaihtelevat valmistajittain — SunSpec-standardia tukevat laitteet kuten monet Fronius- ja SMA-invertterit käyttävät yhtenäistä rekisterikarttaa.

    Kenttäkerros: Sungrow siirtää paneelien toimintapisteen pois maksimitehopisteestä. MPPT-jännite nousee 501 V:sta 558 V:iin — invertteri ottaa vähemmän virtaa paneeleilta.

    Havaintokerros: InfluxDB tallentaa koko tapahtumaketjun syineen. Grafana näyttää sen kaavioina.

    Jokainen kerros teki oman osuutensa. Kukaan ei astunut toisen tontille.

    Miksi tämä on tärkeämpää kuin miltä näyttää

    Useimmat kotiautomaatiojärjestelmät rakentuvat yksittäisten automaatioiden varaan. ”Jos aurinko tuottaa yli 2 kW ja hinta on alle 5 senttiä, lataa auto.” Tämä toimii kunnes tulee tilanne jota ei osattu ennakoida — ja niitä tulee aina.

    Kerrosmalli ei poista yllätyksiä. Mutta se tekee niistä hallittavia. Kun jotain menee väärin, tiedetään missä kerroksessa etsiä. Kun logiikkaa pitää muuttaa, tiedetään mihin koskea. Kun järjestelmä laajenee — akku, V2H, tehomaksu — uusi komponentti liitetään oikeaan kerrokseen ilman että koskaan tarvitsee kirjoittaa kaikkea uudelleen.

    Se on EnergyHubin ydinajatus: rakenna infrastruktuuri, ei automaatio.


    Seuraavaksi: Osa 5 — Prioriteetit, tilakoneet ja fallback. Miten järjestelmä päättää kuka saa sähköä kun kaikki haluavat sitä yhtä aikaa.

    Tekninen toteutus: MQTT-rakenne ja Node-REDin integraatio on kuvattu tarkemmin ohjeessa HA:n valmistelu integraatiokerrokseksi.

  • Case: oma talo — Osa 3: Home Assistant integraatiokerroksena

    Home Assistantin roolia EnergyHubin integraatiokerroksena kuvaava grafiikka

    Tämä on kolmas osa sarjasta, joka dokumentoi EnergyHub-järjestelmän rakentamista vanhaan rintamamiestaloon. Edellisessä osassa käytiin läpi arkkitehtuuripäätökset ennen koodia: miksi järjestelmä jaettiin vastuualueisiin ja mihin järjestelmätason päätöksenteko sijoitetaan. Tässä osassa tarkastelen sitä, mitä Home Assistantin rooli integraatiokerroksena tarkoitti käytännössä.


    Kun EnergyHub-arkkitehtuuria suunniteltiin, yksi kysymys nousi toistuvasti esiin: pitääkö Home Assistantin päättää vai välittää?

    Vanhassa järjestelmässä HA teki molempia. Se luki arvot, vertasi kynnysarvoihin ja ohjasi laitteita samojen automaatioiden sisällä. Tämä toimi pitkään, kunnes aurinkotuotanto, sähkön hinta, sähköauton lataus ja lämpöpumpun käyttö alkoivat vaikuttaa toisiinsa.

    Ongelma ei ollut se, etteikö Home Assistantilla voisi tehdä monimutkaista logiikkaa. Ongelma oli, että tämän talon järjestelmätason priorisointipäätökset olivat vuosien aikana hajonneet eri automaatioihin.

    EnergyHubissa päätös oli siksi selkeä: Home Assistant ei omista energian optimointiin liittyvää järjestelmätason priorisointia.

    Arkkitehtuuri yhdellä silmäyksellä

    ┌─────────────────────────────────────────┐
    │  Kenttä- ja laitetaso                    │
    │  Sungrow · Thermia · Shelly · P1         │
    └────────────────────┬────────────────────┘
                         │ lukee / ohjaa
    ┌────────────────────▼────────────────────┐
    │  Home Assistant                         │
    │  Integraatiot · normalisointi · tila    │
    │  käyttöliittymä · paikallinen logiikka  │
    └──────────┬──────────────────┬───────────┘
               │ telemetria       │ ohjauspyynnöt
               ▼                  ▲
    ┌─────────────────────────────────────────┐
    │  Node-RED                               │
    │  priorisointi · päätös · orkestrointi   │
    └─────────────────────────────────────────┘

    Tässä vaiheessa työnjako hahmotettiin tarkoituksella yksinkertaisena. Home Assistant yhdistää laitteet yhteiseen tilannekuvaan. Node-REDiin keskitetään talon eri tavoitteiden välinen päätöksenteko. Fyysinen laite säilyttää oman sisäisen säätönsä ja suojauksensa.

    Myöhemmissä Case-osissa rajat tarkentuivat edelleen. Tämä oli lähtökohta, jonka avulla vanha hajautunut automaatiomalli saatiin ensin purettua ymmärrettäviksi vastuiksi.

    Mitä integraatiokerros tässä projektissa tarkoittaa?

    Home Assistantin ensimmäinen tehtävä on lukea laitteiden tilat Modbusin, P1-mittauksen ja muiden integraatioiden kautta. Toinen tehtävä on muuttaa eri lähteiden tiedot sellaiseen yhteiseen muotoon, jota muu EnergyHub voi käyttää ilman laitekohtaista tulkintaa.

    Kolmas tehtävä on toimia osana ohjauspolkua. Ylemmän tason päätös voidaan välittää Home Assistantin kautta laitteelle, mutta päätös ja fyysinen toteutus pidetään käsitteellisesti erillään.

    HA voi sisältää myös determinististä paikallista logiikkaa: käyttötiloja, datan validointia, aikakatkaisuja tai käyttäjän ohitusten käsittelyä. Rajaus ei siis tarkoita, että HA olisi passiivinen viestiputki. Rajaus koskee sitä, missä koko talon tavoitteiden väliset kompromissit ratkaistaan.

    Miksi vastuunjako kannattaa?

    Ensimmäinen hyöty on vianhaku. Kun jotain menee väärin, voidaan seurata ketjua vaihe vaiheelta:

    lähdelaite
    → HA:n lukema
    → kanoninen mittaus
    → Node-REDin päätös
    → ohjauspyyntö
    → laitteen readback
    → fyysinen vaste

    Jos lähdelaitteen tieto on oikein mutta kanoninen mittaus väärin, ongelma on normalisoinnissa. Jos tilannekuva on oikein mutta päätös väärä, vikaa etsitään päätöksentekologiikasta. Jos komento lähetettiin mutta fyysistä vastetta ei synny, ongelma voi olla ohjauspolussa, laitteessa tai laitteen omissa ehdoissa.

    Tämä on tarkempi tapa ajatella vikaa kuin olettaa, että se kuuluu automaattisesti tietylle kerrokselle.

    Toinen hyöty on testattavuus. Kun rajapinnat ovat määriteltyjä, Node-REDin päätöslogiikkaa voidaan syöttää testidatalla ilman oikeaa laitetta. Samoin Home Assistantin mittauksia ja integraatioita voidaan tarkistaa ilman, että optimointilogiiikka tekee niiden perusteella oikeita ohjauksia.

    Kolmas hyöty on hallittu vikatila. Yhden komponentin vika ei saa automaattisesti tarkoittaa sitä, että jokin vanha arvo tai viimeinen komento tulkitaan turvalliseksi. Jokaiselle olennaiselle toiminnolle pitää määritellä erikseen, mitä tapahtuu tiedon, päätöksentekijän tai ohjausyhteyden kadotessa.

    Kanoniset mittaukset erottavat lähteen järjestelmätason tiedosta

    Yksi konkreettinen ratkaisu, joka syntyi arkkitehtuuripäätöksistä, oli kanoninen mittauskerros. Home Assistantissa järjestelmätason mittauksille käytettiin m_-etuliitettä — “measurement”.

    Ajatus oli tärkeämpi kuin nimeämiskäytäntö: raakadata ei ole sama asia kuin järjestelmätason mittaus.

    P1-lähteen teho voi käyttää eri etumerkkisopimusta kuin muu järjestelmä. Hintaintegraatio voi ilmoittaa arvon eri yksikössä. Modbus-arvo voi olla hetken unknown tai puuttua kokonaan yhteyshäiriössä.

    Kanoninen kerros normalisoi tämän. Esimerkiksi:

    sensor.m_grid_power_w
      positiivinen = osto
      negatiivinen = vienti
    
    sensor.m_spot_price_eur_kwh
      aina €/kWh

    Muu järjestelmä käyttää kanonista suuretta eikä laitteen tai integraation alkuperäistä esitystapaa. Jos lähde myöhemmin muuttuu, korjaus tehdään integraatiorajassa eikä jokaiseen päätöksentekosääntöön.

    Pelkkä arvo ei riitä

    Ensimmäisessä toteutusvaiheessa huomio oli pitkälti arvon normalisoinnissa. Myöhemmin sama periaate osoittautui tarpeelliseksi myös tiedon laadulle.

    Järjestelmätason mittauksesta pitää pystyä tietämään esimerkiksi:

    • mistä tieto tuli
    • milloin se havaittiin
    • kuinka vanha se on
    • onko se käyttökelpoinen kyseiseen päätökseen

    Tämä ajatus kasvoi myöhemmin paljon alkuperäistä m_-sensorimallia pidemmälle, mutta sen alku on tässä: päätöksentekijän ei pitäisi joutua tulkitsemaan jokaista raakaintegraatiota erikseen.

    Capability kertoo luvan – ei päätöstä

    Toinen alkuvaiheen rakenne oli capability-ajattelu: erillinen tila kertoo, onko tietyn toiminnon käyttäminen sallittua.

    Esimerkiksi c_ev_charge_allowed voidaan tulkita käyttöluvaksi sähköauton lataukselle. Se ei tarkoita, että auton pitäisi juuri nyt ladata, eikä se ole sama asia kuin fyysiselle releelle annettava komento.

    Tässä erossa on kolme eri kysymystä:

    1. Saako toimintoa käyttää?
    2. Haluaako päätöksenteko käyttää sitä juuri nyt?
    3. Toteutuiko pyydetty toiminto fyysisesti?

    Tämä erottelu osoittautui myöhemmin olennaiseksi. Yhden boolean-arvon ei pidä yrittää olla samanaikaisesti käyttäjän lupa, optimoinnin päätös ja fyysisen releen tila.

    Operating mode kokoaa käyttäjän tavoitteen yhteen paikkaan

    Toinen suunnitteluvaiheen ajatus oli keskitetty operating mode eli järjestelmän toimintatila. Sen tarkoitus oli estää tilanne, jossa esimerkiksi poissaolo, tehorajoitus ja normaali hintaoptimointi toteutetaan toisistaan tietämättöminä automaatioina.

    Alkuvaiheessa käytin tiloista nimiä kuten normal, peak_protection, vacation ja emergency. Myöhemmässä toteutuksessa nimet ja tarkka vastuunjako muuttuivat, mutta periaate säilyi: järjestelmällä pitää olla eksplisiittinen tapa ilmaista, mikä käyttötilanne on voimassa ja mikä tavoite ohittaa normaalin optimoinnin.

    Home Assistant oli luonteva paikka tehdä käyttötilasta käyttäjälle näkyvä ja säilyttää sen järjestelmätason tila. Päätöksentekologiikka puolestaan käyttää tätä yhtenä syötteenään.

    Tärkeää on, ettei toimintatila saa yksin ohittaa fyysisiä suoja- tai laiterajoja. Esimerkiksi käyttäjän pyytämä “lataa nyt” voi ohittaa normaalin hintapäätöksen, mutta ei liittymän sallittua kuormaa.

    Mitä Home Assistant ei tässä EnergyHubissa omista?

    Tässä projektissa HA:han ei haluttu keskittää päätöstä siitä, onko juuri tämä vartti kokonaisuuden kannalta paras sähköauton lataukseen tai kuinka lämpöpumpun, auton ja muun jouston prioriteetit pitäisi sovittaa yhteen.

    Nämä päätökset keskitettiin Node-REDiin, jossa yhteinen päätöspolku oli helpompi nähdä ja testata.

    Tämä ei tarkoita, ettei HA:ssa olisi lainkaan päätöksiä. Paikalliset tilakoneet, käyttötilat, datan validointi ja muut deterministiset säännöt voivat aivan hyvin kuulua integraatio- ja automaatiokerrokseen.

    Rajanveto on siis tämä: HA ei tässä toteutuksessa omista koko talon monen tavoitteen optimointia.

    Uudelleenkäynnistyminen paljasti tärkeän eron tilan ja historian välillä

    Alkuperäisessä suunnitelmassa ajatus oli, että Node-RED voi käynnistyä uudelleen, lukea Home Assistantin nykytilan ja jatkaa siitä. Tämä on hyvä tavoite, mutta lause “historiaa ei tarvita” oli liian vahva.

    Normaalin päätöksenteon ei pitäisi riippua siitä, että koko vanha tapahtumahistoria ladataan muistista jokaisessa restartissa. Järjestelmän pitää kuitenkin säilyttää tai pystyä rekonstruoimaan sellainen tila, jolla on merkitystä tulevalle käyttäytymiselle.

    Tällaista tilaa voivat olla esimerkiksi:

    • aktiivinen käyttötila
    • käyttäjän voimassa oleva tilapäinen ohitus
    • hystereesiin tai vähimmäiskestoon liittyvä tieto
    • sellainen määräaika, jota ei saa unohtaa restartissa

    Sen lisäksi historiadataa tarvitaan aivan eri tarkoitukseen: päätösten jäljitettävyyteen, vianhakuun, datan laadun arviointiin ja myöhemmin mallintamiseen.

    Tämä ero — päätöksenteon tarvitsema nykytila vs. auditointiin tarvittava historia — tarkentui vasta järjestelmän rakentamisen edetessä.

    Mitä tapahtuu, jos Node-RED pysähtyy?

    Myös tähän alkuperäinen vastaus oli liian yksinkertainen: “laitteet jäävät viimeiseen tunnettuun turvalliseen tilaan”. Viimeinen tunnettu tila ei välttämättä ole turvallinen tai tarkoituksenmukainen pitkään jatkuvassa häiriössä.

    Oikea suunnittelukysymys on laitekohtainen: mikä on hyväksyttävä fallback, jos ylempi päätöksenteko katoaa?

    • voiko laite palata omaan normaaliin säätöönsä
    • pitääkö tilapäinen optimointipyyntö purkaa
    • saako jokin kuorma jäädä päälle määräajaksi
    • miten HA tai muu paikallinen kerros tunnistaa päätöksentekijän katoamisen

    Tämä ajatus johti myöhemmissä Case-osissa heartbeat-, watchdog-, sallittavuus- ja final gate -ratkaisuihin. Tässä vaiheessa tärkeä päätös oli vasta se, että vikatilaa ei jätetä sattuman varaan.

    Käytännön toteutus Home Assistant Containerilla

    EnergyHubissa Home Assistant pyörii Docker-kontissa Lenovo ThinkCentrellä. Käytössä ei ole Home Assistant OS:ää vaan Home Assistant Container.

    Tämä antaa täyden hallinnan samalle Debian-koneelle rakennettavasta Docker-kokonaisuudesta, mutta samalla HAOS:n Supervisor- ja add-on-malli ei ole käytössä. Tarvittavat muut palvelut, kuten MQTT-välittäjä, ajetaan omina kontteinaan. Yhteisön custom integration -integraatioita voidaan puolestaan asentaa HACS:n kautta.

    Pakettipohjaisella konfiguraatiolla eri vastuut erotettiin jo tiedostotasolla:

    • 00_core.yaml — järjestelmäparametrit ja yhteiset tilat
    • 10_integrations.yaml — laite- ja tietolähdeintegraatiot
    • 20_measurements.yaml — kanoniset m_-sensorit
    • 30_load_ev.yaml — sähköautoon liittyvä paikallinen tila ja ohjausrajapinta
    • 31_load_heatpump.yaml — lämpöpumpun ohjausrajapinta
    • 40_optimization.yaml — rajapinta Node-REDin suuntaan
    • 50_reporting.yaml — energia- ja raportointitiedot

    Kaikki tämän vaiheen tiedostonimet tai vastuut eivät jääneet myöhemmin muuttumattomiksi. Olennaisempaa oli, että integraatiot, mittaukset, ohjausrajapinnat ja raportointi alettiin erottaa jo konfiguraation rakenteessa.

    Tekninen toteutus on kuvattu tarkemmin ohjeessa Home Assistantin valmistelu integraatiokerrokseksi.

    Tämän vaiheen tärkein oppi

    Home Assistantin arvo EnergyHubissa ei vähentynyt, kun siltä poistettiin järjestelmätason optimoinnin omistajuus. Päinvastoin sen rooli selkeytyi.

    Se toimii rajana laitteiden kirjavan maailman ja EnergyHubin yhteisen tilamallin välillä. Kun mittaukset, käyttötilat ja ohjausrajapinnat ovat yhdessä paikassa määriteltyjä, ylempi päätöksenteko voi keskittyä siihen, mitä sen oikeasti pitää ratkaista.

    Integraatiokerroksen tehtävä ei ole olla mahdollisimman yksinkertainen. Sen tehtävä on tehdä muun järjestelmän riippuvuuksista hallittavia.

    Seuraavaksi: kerrosmalli käytännössä

    Osa 4 käy läpi kerrosmallin kokonaisuudessaan: miten Home Assistant, Node-RED ja kenttätaso keskustelevat keskenään ja mikä MQTT:n rooli tässä toteutuksessa on.

    HEOMF Case: Oma talo -sarjan pääsivulta löydät kaikki sarjan osat.

  • Case: oma talo — Osa 2: Arkkitehtuuripäätökset: mitä päätettiin ennen koodia

    EnergyHubin arkkitehtuuripäätöksiä ennen varsinaisen toteutuksen aloittamista kuvaava grafiikka

    Ensimmäinen reaktio ongelmaan oli väärä.

    Kun Osa 0:ssa kuvattu tilanne alkoi olla selvä — järjestelmä oli kasvanut hallitsemattomaksi, ohjausvastuu oli epäselvä eikä uusia ominaisuuksia enää uskaltanut lisätä huoletta — ensimmäinen ajatus oli siivota Home Assistant.

    Ajatus vaikutti loogiselta. Automaatioita voisi järjestellä paketteihin, nimeämiskäytäntöjä yhtenäistää ja YAML:ia selkeyttää. Kaikki tuntui korjattavissa olevalta, kunhan kokonaisuuden vain kävisi kunnolla läpi.

    Kävin läpi.

    Mitä enemmän kokonaisuutta tutkin, sitä selvemmäksi yksi asia tuli: ongelma ei ollut YAML.

    Ongelma oli siinä, mihin päätöksenteko oli kertynyt

    Home Assistantiin oli vuosien varrella kertynyt paljon muutakin kuin integraatioita ja käyttöliittymää. Samassa kokonaisuudessa ratkaistiin esimerkiksi:

    • milloin auto lataa
    • mitä kuormaa priorisoidaan
    • milloin aurinkoylijäämä menee hinnan edelle
    • milloin tehoraja ohittaa muut tavoitteet

    Nämä ovat järjestelmätason päätöksiä. Ongelma ei ollut se, etteikö Home Assistantilla voisi toteuttaa logiikkaa, vaan se, että päätökset olivat hajallaan eri automaatioissa ilman yhtä yhteistä priorisointimallia.

    Kun yksi automaatio päätti käynnistää latauksen, toinen saattoi samaan aikaan yrittää rajoittaa sitä. Molemmat saattoivat olla yksittäin järkeviä, mutta kumpikaan ei omistanut kokonaisuutta.

    Tässä projektissa päätin siksi erottaa Home Assistantin roolin järjestelmätason optimoinnista. Home Assistant jäi integraatioiden, tilan seurannan ja käyttöliittymän keskeiseksi alustaksi, mutta koko talon priorisointia ei enää rakennettaisi hajalleen sen erillisiin automaatioihin.

    Pelkkä vanhan rakenteen siistiminen olisi tehnyt samasta vastuunjaosta siistimmän näköisen. Se ei olisi ratkaissut itse ongelmaa.

    Tärkein yksittäinen päätös

    Tärkein arkkitehtuuripäätös koko projektissa oli tämä:

    Tässä EnergyHubissa Home Assistant ei enää omista järjestelmätason optimointia.

    Se kerää ja välittää tietoa, hallitsee integraatioita, tarjoaa käyttöliittymän ja toimii osana ohjauspolkua. Päätökset siitä, milloin kuormia kannattaa käyttää, mitä priorisoidaan ja miten ristiriitaiset tavoitteet sovitetaan yhteen, keskitetään omaan päätöksentekologiikkaansa.

    Tämä yksi päätös selkeytti paljon muuta. Kun vastuut ovat näkyviä, on helpompi päättää, kuuluuko uusi toiminto laiteintegraatioon, automaatioon, optimointiin vai fyysisen ohjauksen sallittavuuteen.

    HEOMF:n optimoinnin arkkitehtuurissa sama periaate näkyy loogisten vastuiden erottamisena: integraatio, automaatio, optimointi ja fyysinen toteutus eivät ole sama tehtävä, vaikka osa niistä pyörisi samalla teknisellä alustalla.

    Miltä työnjako näytti ennen ensimmäistä koodiriviä?

    Projektin toteutusta varten jaoin vastuut neljään käytännön kokonaisuuteen. Tämä oli toteutustapa, ei vaatimus siitä, että jokaisen vastuun pitäisi aina olla oma erillinen ohjelmistonsa tai fyysinen laitteensa.

    Kenttä- ja laitetaso

    Täällä ovat fyysiset laitteet ja niiden suorat rajapinnat: Shelly-releet, lämpöpumpun ja invertterin Modbus-yhteydet, energiamittaukset ja laitteiden omat säätimet.

    Tärkeä tavoite oli, etteivät laitteiden omat turvalliset käyttörajat riippuisi siitä, että ylemmän tason optimointi toimii juuri sillä hetkellä. Se ei tarkoita, että koko kenttätaso olisi täysin itsenäinen kaikissa häiriöissä, vaan että vian vaikutus pyritään rajaamaan mahdollisimman lähelle sitä toimintoa, jota se koskee.

    Integraatio ja tilannekuva

    Home Assistantin tehtäväksi jäi laiteintegraatioiden kokoaminen, mittausten ja tilojen tarjoaminen muulle järjestelmälle sekä käyttöliittymä.

    Ajatus ei ollut tehdä HA:sta passiivista putkea. Se voi sisältää myös automaatiota ja paikallista tilalogiikkaa. Rajaus koski ennen kaikkea sitä, ettei koko talon optimointipäätöksiä enää hajautettaisi ympäri järjestelmää.

    Päätöksenteko ja orkestrointi

    Node-RED valittiin tässä projektissa paikaksi, johon järjestelmätason päätöksentekoa alettiin keskittää. Sen tehtäväksi suunniteltiin hinnan, aurinkotuotannon, tehon, lämpötilojen ja käyttäjän tavoitteiden yhdistäminen yhteiseen päätöspolkuun.

    Tässä kerroksessa ratkaistaan esimerkiksi, milloin autoa kannattaa ladata, milloin lämpöpumpulle annetaan poikkeava tavoite ja mikä kuorma väistää, jos kaikkia tavoitteita ei voida toteuttaa samaan aikaan.

    Observointi ja historiatieto

    Aikasarjatietokannan ja visualisoinnin tehtäväksi suunniteltiin muutakin kuin energiankulutuksen näyttäminen. Järjestelmästä piti myöhemmin pystyä tarkistamaan myös päätöksiä ja niiden seurauksia.

    Tätä ei kannata ajatella täysin erillisenä ohjauskerroksena, vaan toimintona, joka kulkee muiden vastuiden rinnalla. Ilman historiatietoa ja diagnostiikkaa hiljainen vika voi näyttää pitkään normaalilta toiminnalta.

    MQTT rajapinnaksi kerrosten väliin

    Yksi keskeisistä suunnittelupäätöksistä oli käyttää MQTT:tä sovituissa kohdissa järjestelmän sisäisenä viestiväylänä. Tarkoitus oli vähentää suoria riippuvuuksia niin, ettei päätöksentekologiikan tarvitse tuntea jokaisen laitteen omaa rajapintaa.

    Suunnitelman mukaisia aiheita olivat esimerkiksi:

    energyhub/system/safety_trip
    energyhub/command/ev/allowed

    MQTT ei itsessään takaa, että vastaanottajan ollessa poissa jokainen viesti automaattisesti odottaa ja toimitetaan myöhemmin. Se riippuu muun muassa QoS-tasosta, retained-viesteistä, sessiosta ja sovelluksen omasta suunnittelusta.

    Arkkitehtuuripäätös oli siksi laajempi kuin “käytetään MQTT:tä”: viestien merkitys, säilyvyys, vanheneminen ja vastaanottajan käyttäytyminen piti määritellä erikseen.

    Tavoite oli, että yhden komponentin vaihto tai uudelleenkäynnistys ei pakota kirjoittamaan koko järjestelmän logiikkaa uudelleen.

    Miksi juuri Node-RED?

    Kysymys ei ollut siitä, etteikö Home Assistantin automaatioilla voisi rakentaa monimutkaistakin päätöksentekoa. Tässä projektissa Node-RED sopi kuitenkin paremmin siihen tapaan, jolla halusin nähdä ja testata päätöspolun.

    Energiaoptimointi ei ollut enää vain yksittäinen “jos X, tee Y” -reaktio. Päätöksessä saatettiin joutua huomioimaan yhtä aikaa esimerkiksi hinta, aurinkotuotanto, kokonaisteho, lämpötila ja käyttäjän asettama tavoite.

    Node-REDissä tällaisen päätösketjun pystyi rakentamaan näkyväksi flow’ksi, jossa lähtötiedot, ehdot, prioriteetit ja lopputulos voitiin erottaa toisistaan.

    Toinen syy oli vikatilanteiden eksplisiittinen käsittely. Puuttuva hintatieto, vanhentunut mittaus tai vastaamaton laite piti saada omaksi tunnetuksi tilakseen sen sijaan, että automaatio vain jäisi hiljaa tekemättä.

    Tämä oli siis EnergyHubin toteutusvalinta, ei yleinen väite siitä, että Node-RED olisi optimointiin aina Home Assistantia parempi.

    Miksi kriittinen päätöspolku haluttiin paikalliseksi?

    Kolmas keskeinen päätös oli sijoittaa EnergyHubin varsinainen päätöksenteko paikalliselle Lenovo ThinkCentre -koneelle Debianin ja Docker Composen päälle.

    Tavoitteena ei ollut poistaa pilveä järjestelmästä. Hintatiedot, sääennusteet ja osa laitekohtaisesta datasta tulevat joka tapauksessa ulkoisista palveluista.

    Sen sijaan halusin, ettei talon sisäinen päätöspolku olisi tarpeettomasti riippuvainen jatkuvasta internet-yhteydestä. Paikalliset mittaukset, laiteintegraatiot ja keskeinen ohjaus pystyvät toimimaan paikallisessa verkossa, vaikka uusi ulkoinen tieto olisi hetken poissa.

    Tämä ei vielä tee järjestelmästä automaattisesti vikasietoista. Myös Lenovo, paikallinen verkko tai oma ohjelmisto voivat vikaantua. Siksi myöhempi työ joutui käsittelemään erikseen fallbackit, watchdogit, datan tuoreuden ja fyysisen toteuman verifioinnin.

    Mitä päätettiin jo tässä vaiheessa jättää ratkaisematta?

    Arkkitehtuuripäätöksiä tehdessä oli tärkeää erottaa rakenne varsinaisesta optimointilogiikasta.

    Kerrosmalli ei vielä kerro, mikä on oikea sähköauton latausraja, kuinka paljon lämmitystä kannattaa siirtää tai mikä ennustemalli toimii parhaiten. Se ei myöskään tee järjestelmästä luotettavaa vain siksi, että komponentit on nimetty eri kerroksiin.

    Arkkitehtuurin tehtävä oli tehdä nämä myöhemmät ongelmat ratkaistaviksi yksi kerrallaan:

    • päätöslogiikka voidaan muuttaa ilman että laiteintegraatio kirjoitetaan uudelleen
    • uusi mittaus voidaan lisätä yhteiseen tilannekuvaan ilman että jokainen ohjaus lukee sitä suoraan
    • vikatilanteen paikka voidaan rajata paremmin
    • uuden ominaisuuden vastuu voidaan määrittää ennen toteutusta

    Juuri tätä varten arkkitehtuuri tehtiin ennen koodia.

    Yksi päätös vaikutti kaikkeen myöhempään

    Jälkikäteen katsottuna tärkein ratkaisu ei ollut Node-RED, MQTT, Debian eikä Lenovo. Ne olivat toteutusteknologioita.

    Tärkein ratkaisu oli päättää, ettei koko järjestelmä enää kasva yhden työkalun sisällä sen mukaan, mihin uusi automaatio sattuu helpoimmin syntymään.

    Päätöksenteko, integraatio, fyysinen toteutus ja havaittavuus saivat omat vastuunsa.

    Myöhemmät osat näyttävät, että rajat eivät pysyneet käytännössä aivan näin yksinkertaisina. Se oli odotettavissa. Olennaisempaa oli, että järjestelmällä oli ensimmäistä kertaa rakenne, jonka sisällä näitä rajoja voitiin tarkentaa ilman että palattiin vanhaan hajautettuun automaatiomalliin.

    Seuraavaksi: Home Assistant integraatiokerroksena

    Osa 3: Home Assistant integraatiokerroksena käsittelee sitä, miten entiteettimalli rakennettiin, mitä integraatioita tarvittiin ja miten rajapinta päätöksentekokerrokseen toteutettiin käytännössä.

    Tekninen perusalusta on kuvattu erillisessä Debian + Docker + Home Assistant + Node-RED -ohjeessa.

    HEOMF Case: Oma talo -sarjan pääsivulta löydät kaikki sarjan osat.