Case: oma talo — Osa 22: Voiko dataan luottaa? EnergyHub alkaa rakentaa aineistoa, ei vain ohjata

Koko tämä sarja on rakentanut järjestelmää, joka tekee päätöksiä. Se päättää milloin autoa ladataan, milloin käyttövettä lämmitetään, milloin lämpöpumppu saa boostata. Ja jokainen niistä päätöksistä on täsmälleen niin hyvä kuin data, jonka varaan se rakennetaan. Seuraava iso kehitysaskel — ennustepohjainen lämmitys, joka katsoo säätä, hintoja ja talon lämpömassaa — ei ole rakennettavissa ennen kuin sen alla on aineistoa, johon voi luottaa. Tämä osa kertoo siitä työstä, jolla raakatelemetriasta tehdään mallikelpoista dataa. Ja se on osoittautunut vaikeammaksi kuin itse ohjaus.

Sanon heti aluksi, että tämä osa on toisenlainen kuin edelliset. Aiemmat kertoivat valmiista, tuotannossa todennetuista ominaisuuksista. Tämä kertoo työstä, joka on parhaillaan käynnissä: osa siitä on jo asennettuna ja todennettuna, osa odottaa vielä commitointia ja 24 tunnin vakausikkunaa, osa on vasta suunniteltu. Kirjoitan tämän nyt, koska työn opetukset ovat kiinnostavampia kuin sen lopputila — ja koska yksi näistä opetuksista näkyi jo osassa 20, kun Voimapirtin Lämpö-sivun huoneanturit näyttivät ”Ei tietoa”. Ne olivat hiljaa juuri tämän työn takia.

Miksi juuri nyt: talvidata ei odota

Ajoitus ei ole sattumaa. Lämmitysmallia varten tarvitaan talvidataa — sitä, miten talo käyttäytyy kun lämpöpumppu oikeasti lämmittää. Kesällä maalämpö tekee vain käyttöveden ja kylpyhuoneen lattialämmön, tunti pari vuorokaudessa. Lämmityskausi alkaa vasta kun ulkolämpötila painuu pysyvästi kynnyksen alle, ja kirjoitushetkellä seitsemän vuorokauden keskiarvo on 19,4 °C, kun kynnys on 12 °C. Siitä kertyy karkeasti kahdeksasta kymmeneen viikkoa aikaa saada tallennus kuntoon.

Lämmitysdataa ei voi kerätä jälkikäteen. Jos tallennus ei ole valmis ja todennettu ennen ensimmäisiä pakkasjaksoja, se talvi menee ohi. Malli, joka olisi voinut oppia tästä talvesta, joutuisi odottamaan seuraavaa. Tämä on koko työn kiireen todellinen syy: kyse ei ole siitä, että data pitäisi saada nopeasti, vaan siitä, että sitä ei saa lainkaan, jos ikkuna menee kiinni tyhjänä.

Kaksi kerrosta: raaka säilyttää haamut, tila suodattaa ne pois

Ensimmäinen tärkeä ratkaisu oli jakaa data kahteen kerrokseen, joilla on eri tehtävä ja eri sääntö.

Raakakerros säilyttää kaiken, myös virheelliseltä näyttävän. Se on auditointia varten. Jos anturi julkaisee epäilyttävän arvon, raakakerros tallentaa sen sellaisenaan, koska myöhemmin voi olla tarpeen katsoa, mitä oikeasti tuli linjaa pitkin. Raakakerrosta ei saa siivota, korjata eikä täydentää — se on todistusaineisto, ei tulkinta.

Tilakerros on laatusuodatettu, ja se on se, jonka varaan malli rakennetaan. Siinä jokainen havainto on luokiteltu käyttökelpoiseksi tai ei, ja käyttökelvoton havainto ei valu malliin mukana. Sama fyysinen mittaus on siis tietokannassa kahdesti eri roolissa: kerran haamuineen todistusaineistona, kerran puhdistettuna päätöksentekoa varten.

Ero kuulostaa akateemiselta, mutta se ratkaisee käytännön ongelman. Ilman raakakerrosta jokainen laatusääntö olisi peruuttamaton: kun havainto on kerran hylätty, sitä ei enää saa takaisin tarkistettavaksi. Ilman tilakerrosta taas malli joutuisi luottamaan kaikkeen, myös rikkinäiseen. Kaksi kerrosta antaa molemmat: näkyvyyden menneeseen ja luotettavuuden tulevaan.

Hiljainen data-aukko on vaarallisempi kuin näkyvä virhe

Tämän työn opettavaisin yksittäinen löytö oli rikkinäinen ulkolämpötila-anturi. Talon Netatmo-ulkomoduuli on ollut pitkään viallinen niin, että sovellus näyttää siitä lukeman, mutta lukema ei ole totta. Se ei näy tyhjänä, ei virheenä, ei vanhentuneena — se näyttää täysin uskottavalta arvolta, jonka ikä on vain 21 sekuntia. Juuri sellaiselta, johon malli mielellään luottaisi.

Näkyvä virhe on turvallisempi kuin uskottava valhe. Tyhjän ruudun huomaa. Vanhentuneen aikaleiman huomaa. Mutta tuoreelta näyttävä väärä arvo menee suoraan läpi, ja päätös rakentuu sen varaan ilman että kukaan huomaa mitään. Malli, joka saisi tämän anturin arvon, oppisi talvesta väärän tarinan eikä koskaan kertoisi tehneensä niin.

Ratkaisu oli erottaa kytkeytyneisyys lukemasta. Jokaisella anturilla on erillinen tieto siitä, onko yhteys oikeasti pystyssä, ja connected=0 pakottaa laadun tilaan unavailable riippumatta siitä, miltä itse lukema ja sen ikä näyttävät. Rikkinäinen ulkoanturi merkitään käyttökelvottomaksi, vaikka se julkaisisi kuinka uskottavaa lukemaa. Ainoa oikeasti toimiva ulkolämpötilan lähde on tällä hetkellä lämpöpumppu itse, ja tilakerros nojaa siihen.

Puuttuva, nolla ja tuntematon ovat kolme eri asiaa

Sama periaate, joka kulki Voimapirtin läpi osassa 20viiva ei ole nolla — pätee tallennukseen vielä tiukemmin. Kun anturi ei kerro arvoaan, se tieto tallennetaan tyhjänä, null, ei koskaan nollana. Nolla on mittaustulos. Tyhjä on mittauksen puuttuminen. Jos nämä sekoittuvat, malli oppii, että ulkona oli nolla astetta silloin kun anturi oli oikeasti mykkä, ja se on suoraan väärää oppia.

Kolmas tila on tuntematon: arvo, jota ei syystä tai toisesta voi luokitella luotettavaksi. Se ei ole sama kuin puuttuva eikä sama kuin nolla. Näiden kolmen erottaminen läpi koko ketjun — anturilta MQTT:n kautta tietokantaan — on työn tylsin ja tärkein osa. Suurin osa datalaadun bugeista, jotka tässä työssä löytyi, oli juuri näiden kolmen sekoittumista jossain kohtaa putkea.

Ikä on osa mittausta

Osassa 18 opittiin kalliisti, että päätös vanhalla datalla on väärä päätös, vaikka data olisi joskus ollut oikea. Sama pätee tallennukseen. Jokaisella kentällä on oma tuoreutensa, ja se tuoreus tallennetaan mittauksen mukana, ei erikseen arvattavaksi.

Eri suureilla on eri tahti. Lämpöpumpun prioriteettitieto ja kompressorin nopeus päivittyvät kolmenkymmenen sekunnin välein, menoveden lämpötila ja mukavuuspyörän lukema minuutin välein, lisävastuksen tila kymmenen sekunnin välein, käyttöveden asetusarvot vain viiden minuutin välein. Jos kaikkia kohdeltaisiin samalla tuoreusrajalla, joko tuoreet arvot hylättäisiin turhaan tai vanhat hyväksyttäisiin liian pitkään. Siksi lämpöpumpun kentät on ryhmitelty tuoreuden mukaan omiin failsafe-rajoihinsa, karkeasti 90 ja 180 sekuntia sen mukaan kuinka nopeasti suure oikeasti muuttuu.

Tämä on sama oivallus kuin readback-työssä osassa 18, mutta nyt data-auditoinnin tasolla: aikaleima ei ole metatietoa mittauksen vieressä, se on osa itse mittausta. Arvo ilman ikäänsä ei ole havainto, se on huhu.

Sama periaate kantaa jo ohjauspuolellekin. Home Assistantiin rakennettiin oma canonical-julkaisija fyysisille SG-releille, ja se erottaa toisistaan sen, milloin releen tila todella havaittiin ja milloin tilannekuva julkaistiin, eikä heartbeat saa vanhentunutta havaintoa näyttämään uudelta. Se, milloin rele oikeasti raportoi tilansa, ja se, milloin julkaisija todistaa olevansa elossa, ovat kaksi eri aikaa, eikä jälkimmäinen saa teeskennellä edellistä. Ikä on siis osa mittausta yhtä lailla silloin, kun kysytään mitä rele teki, kuin silloin, kun kysytään kuinka lämmin ulkona oli.

Mitä työ opetti bugien kautta

Teoria on siistiä, mutta tallennus opetti käytännössä. Muutama todellinen vika kannattaa kertoa, koska ne näyttävät, miten monella tavalla data voi valehdella onnistuneelta näyttäessään.

Kirjoitus onnistui ei ole sama kuin data on luettavissa. Node-REDin kirjoitustoken oli tietoturvasyistä rajattu pelkkään kirjoitukseen — sama A6-vaiheen minimioikeusperiaate, joka on kulkenut sarjassa mukana. Kirjoitus palautti siististi koodin 204, ”onnistui”, mutta verifiointi, joka luki dataa takaisin samalla tokenilla, sai 404, ”ei löydy”. Data oli tallessa; lukuoikeutta ei ollut. Opetus on täsmälleen sama ”komento ≠ toteuma” -periaate, joka on kulkenut koko sarjan läpi: onnistunut kirjoitus on todennettava lukemalla, eri oikeuksilla kuin kirjoitettiin.

Rivi voi syntyä ilman että siinä on mitään. Eräs varhainen versio kirjoittajasta tuotti tietokantaan rivin myös silloin, kun anturi puuttui kokonaan viestistä — pakolliset kentät oli kovakoodattu, joten rivi näytti kelvolliselta vaikka se oli tyhjä kuori. Korjaus oli tehdä kytkeytyneisyydestä pakollinen kenttä: näin puuttuva anturi hylkää koko erän, mutta aidosti käyttökelvoton havainto (rikkinäinen ulkoanturi, connected=0, lukema tyhjä) tallentuu silti — juuri niin kuin kaksikerrosmalli vaatii.

Aikaleiman tarkkuus voi ylittää lukujen rajat. Tietokantaan tarjottu nanosekuntitarkka aikaleima oli suurempi kuin se kokonaisluku, jonka JavaScript pystyy turvallisesti esittämään. Sama bugi löytyi myöhemmin toisesta, jo tuotannossa olleesta kirjoittajasta. Aikaleima korjattiin millisekuntitarkkuuteen ja sen turvallisuus tarkistetaan erikseen ennen kirjoitusta.

Muisti vuotaa hiljaa, jos sitä ei rajata. Kirjoittaja piti muistissa karttaa vahvistetuista näytteistä duplikaattien torjumiseksi. Kartta kasvoi rajatta, koska mikään ei siivonnut sitä. Korjaus oli antaa merkinnöille elinaika ja katto: vanhat poistetaan, ja määrälle on yläraja. Ilman tätä järjestelmä olisi hidastunut ja lopulta kaatunut viikkojen mittaan — hitaasti, ilman selvää syytä.

Nämä eivät ole näyttäviä vikoja. Ne ovat juuri sitä hiljaista lajia, joka ei kaada mitään heti mutta myrkyttää aineiston vähitellen. Ja aineisto on se, jonka varaan koko tuleva malli aiotaan rakentaa.

Mitä vielä puuttuu

Tämä on alustava kuvaus, ja rehellisyyden nimissä iso osa on vielä edessä. Pysyväistallennuksen ensimmäinen kerros odottaa 24 tunnin vakausikkunan läpäisyä ja commitointia. Lämpöpumpun raakadatan pysyväistallennus on jo viety tuotantoon ja sen minuutin kadenssi todennettu, mutta se on vasta ensimmäinen kokonainen kerros. Ohjauspolun raakadata — mitä final gate komensi ja mitä releet oikeasti tekivät — on lohkattu omaksi myöhemmäksi kierroksekseen, koska se vaatii uusia välimuistipolkuja jotka pitää ensin rakentaa ja todentaa. Sen ensimmäiset palaset ovat jo paikoillaan: komentopolun vanha comfort-arvo on poistettu kuolleena arvona ja lämpöpumpun tarkkaan näyteaikaan sidottu shadow-triggeri rakennettu. Mutta koko ketju — Node-REDin rajattu historia, final gaten liitos oikeaan hetkeen ja pysyvä tallennus — on vielä kesken.

Siitä — miten yhdestä lämpöpumpun näytteestä rakennetaan jälkikäteen auditoitava rivi, jossa komento, lupa ja fyysinen reletila ovat oikein ajallisesti kohdistettuja — tulee oma jatko-osansa myöhemmin. Sitä ennen tulevat varsinaiset tilamittaukset, jotka luokittelevat raakadatan käyttökelpoiseksi tai ei — mukaan lukien vaikea kysymys siitä, miten lämmitys-, käyttövesi- ja jälkilämpöjaksot erotetaan toisistaan niin, ettei käyttöveden tuottama korkea menovesi sekoitu lämmitysdataan. Ja lopuksi aukkoanalyysi ja visualisointi, jotka tekevät hiljaisista data-aukoista näkyviä. Vasta kun tämä koko ketju on todennettu talven yli, EnergyHub voi alkaa rakentaa mallia sen päälle.

Sen jälkeen tulevat varsinaiset tilamittaukset, jotka luokittelevat raakadatan käyttökelpoiseksi tai ei — mukaan lukien vaikea kysymys siitä, miten lämmitys-, käyttövesi- ja jälkilämpöjaksot erotetaan toisistaan niin, ettei käyttöveden tuottama korkea menovesi sekoitu lämmitysdataan. Ja lopuksi aukkoanalyysi ja visualisointi, jotka tekevät hiljaisista data-aukoista näkyviä. Vasta kun tämä koko ketju on todennettu talven yli, EnergyHub voi alkaa rakentaa mallia sen päälle.


Seuraavassa osassa palataan konkretiaan aivan toisesta suunnasta. Koko tämä sarja on nojannut yhteen oletukseen, jota ei ole vielä koeteltu: että järjestelmän saa palautettua, jos se hajoaa. Varmuuskopioita otetaan, mutta varmuuskopio ei ole varmuuskopio ennen kuin se on kertaalleen palautettu tyhjälle koneelle ja todistettu toimivaksi. Siitä, mitä palautustestissä oikeasti selviää — ja mikä jää palautumatta — kertoo osa 23.