Case: oma talo — Osa 6

Written by

in

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.

Piditkö artikkelista?

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

Seuraa blogia Blogit.fi:ssä