Voimapirtti: kun teknisesti toimiva paneeli ei vielä ollut hyvä käyttöliittymä

Edellisessä osassa rakennettiin käsiohjauksen turvallinen selkäranka: tabletti, joka ei saa ohjata suoraan yhtään laitetta, vaan lähettää pelkkiä pyyntöjä samaan päätösketjuun kuin automaation omat päätökset. Se toimi. Pyyntö validoitiin, intent syntyi, resolveri päätti ja final gate toimeenpani — ja kaikki tämä noin kolmessasadassa millisekunnissa.
Ja silti se ei ollut valmis käyttöliittymä. Se oli insinöörin näkymä, joka sattui roikkumaan seinällä: ruudulla oli teknisiä tiloja, syykoodeja ja rele-termejä, jotka minä ymmärsin mutta jotka eivät kuulu keittiön seinälle koko perheen käyttöön. Tämä osa kertoo siitä, mitä tapahtui kun edellisen osan pyyntörajapinnan päälle rakennettiin oikea käyttöliittymä — ja miksi se työ oli enemmän rajanvetoa kuin pikselinviilausta.
Paneelilla on nyt nimi, Voimapirtti, ja se on tuotannossa Lenovo Tab M10 -tabletilla keittiön seinällä. Neljä pääsivua — Koti, Sähköauto, Lämpö ja Energia — sekä ylätunnisteesta avautuva Diagnostiikka. Fully Kiosk Browser lukittuna yhteen osoitteeseen, tumma teema, 1280 × 800 -näyttö, luettava metrin päästä. Koko käyttöliittymä on paikallinen: se toimii kotiverkossa ilman pilvipalvelua, ja sen takana on sama Home Assistant– ja Node-RED -pohjainen EnergyHub-kotiautomaatio, jota tämä sarja on rakentanut osa osalta.
Tärkein raja: esityskerros ei ole ohjauskerros
Osassa 19 vedettiin luottamusraja: tabletti on epäluotettava ehdottaja, joka saa pyytää mutta ei päättää. Voimapirtti vetää toisen, yhtä tärkeän rajan: käyttöliittymä on renderöintikerros, ei päätöksentekijä. Se näyttää, pyytää ja vahvistaa. Se ei laske sähkön hinnan perusteella latausvartteja, ei laske sulakerajoja, ei ohjaa Thermia-lämpöpumppua suoraan eikä hallitse minkään incidentin elinkaarta.
Tämä kuulostaa itsestäänselvältä, mutta juuri tämä raja erottaa käyttöliittymän insinöörinäkymästä. Kun paneeli ei omista mitään logiikkaa, sen tehtäväksi jää yksi vaikea asia: kääntää järjestelmän tekninen tila ihmisen ymmärtämäksi tilaksi. Ja se on oma taitolajinsa, jota edellinen tekninen paneeli ei osannut.
Ketju on sama kuin ennenkin. Selain hakee kanonisen tilan Node-REDin tarjoamalta /energyhub/status-reitiltä noin viiden sekunnin välein, hinta- ja energiatelemetrian omalta /api/voimapirtti/energy-reitiltä ja käyttötilan omalta /api/home_mode/status-reitiltä. JavaScript-adapteri muuntaa backendin kentät renderöintisnapshotiksi. Kirjoituspolku menee edelleen napista pyynnöksi:
Voimapirtti-painike → Node-RED HTTP -adapteri → energyhub/manual/request (eh.manual.v1) → B3 request validator → domainin Manual Request Manager / Home Mode Manager → Priority Resolver → final gate → fyysinen komento ja readback → /energyhub/status vahvistaa käyttöliittymälle
Uutta ei ole ketju vaan se, mitä sen molemmissa päissä tehdään: mitä käyttäjälle näytetään ja miten pyyntö puetaan.
Yksi luku, joka opetti koko projektin: HTTP 200 ei ole vahvistus
Jos jokin yksittäinen oivallus kiteyttää eron teknisen paneelin ja käyttöliittymän välillä, se on tämä. Kun käyttäjä painaa ”Lataa nyt”, HTTP-vastaus palaa, 200 tai 202, millisekunneissa. Houkutus on näyttää nappi heti aktiivisena — pyyntöhän meni läpi.
Mutta pyyntö läpi ei ole komento toteutunut. B3 voi hyväksyä pyynnön, Manual Request Manager luoda intentin, ja silti Priority Resolver päättää final gaten ehdoilla toisin — tai rele ja readback päivittyvät vasta sekuntien viiveellä. Siksi Voimapirtti ei koskaan merkitse komentoa aktiiviseksi HTTP-vastauksen perusteella. Painike kulkee tilojen läpi — vapaa, lähetetään, vastaanotettu — ja muuttuu aktiiviseksi vasta kun kanoninen status vahvistaa saman request_id:n. Palvelin omistaa tunnisteen, palauttaa sen 202-vastauksessa, ja selain odottaa näkevänsä juuri sen tunnisteen statuksessa ennen kuin uskoo mitään tapahtuneen.
Tämä on täsmälleen sama ”komento ≠ toteuma” -periaate, joka on kulkenut koko tämän sarjan läpi releistä lähtien. Nyt se päti käyttöliittymään: paneeli näyttää vahvistettua tilaa, ei toiveita. Sama koskee käyttövettä — boost näkyy aktiivisena vasta kun status vahvistaa dhw.manual.active-tilan — ja sisälämpötavoitetta, joka katsotaan tallennetuksi vasta kun status palauttaa saman heating.target_c-arvon takaisin. Miinus ja plus muuttavat vain paikallista luonnosta; mitään ei lähde ennen ”Tallenna tavoite” -painallusta, eikä tavoite näy hyväksyttynä ennen kuin backend vahvistaa sen.
Puuttuva arvo on viiva, ei nolla
Toinen periaate näyttää pikkutarkalta mutta on käyttöpaneelissa turvallisuuskysymys. Jos jokin datalähde ei ole saatavilla, Voimapirtti ei näytä nollaa — se näyttää viivan tai ”Ei tietoa”. Nolla on oikea mittausarvo: 0,0 kW aurinkoa on eri asia kuin ”aurinkodataa ei tule”. Jos puuttuva arvo esitetään nollana, käyttäjä tekee päätöksiä datasta jota ei ole.
Tämä periaate näkyy juuri nyt Voimapirtin Lämpö-sivulla konkreettisemmin kuin haluaisin. Sivun huonelämpölista näyttää neljä Netatmo-huonetta — Keittiö, Olohuone, Makuuhuone, Alakerta — ja kaikkien kohdalla lukee tällä hetkellä ”Ei tietoa”, vaikka Thermia-anturi ja ulkolämpötila päivittyvät normaalisti. Syy ei ole Voimapirtissä. Samat lämpötilalähteet ovat parhaillaan toisen, rinnakkaisen kehitystyön alla, ja niiden telemetria on hetkellisesti poikki. Voimapirtti tekee juuri sen mitä pitää: se ei keksi nollaa eikä näytä vanhaa arvoa tuoreena, vaan kertoo rehellisesti, ettei tietoa nyt ole. Kun lähde palaa, rivit täyttyvät itsestään. Tyhjä kohta ruudulla on tässä ominaisuus, ei vika — ja hyvä muistutus siitä, miksi rinnakkaiset kehitystyöt pidetään tarkasti erillään, mihin palaan lopussa.
Neljä sivua ja se, mitä kullakin näytetään
Koti-sivu on kodin energiavirta: aurinko, koti ja verkko kolmena solmuna, ja virtaus haarautuu sen mukaan, tuottaako talo yli oman kulutuksen vai ottaako verkosta. Yläkulmassa lukee suunta selväkielisenä — ”Aurinko → koti + verkko” — ja oikealla on tiivis tilannekortisto: käyttötila, sähköauton varaustaso, lämpö ja käyttövesi. Alapalkissa päivän neljä energialukemaa: tuotettu, kulutettu, verkosta ja verkkoon. Nämä neljä muodostavat taseen, ja se tase tarkistetaan — tuotettu plus verkosta on kulutettu plus verkkoon.

Sähköauto-sivu näyttää varaustason, latausvalmiuden, hinnan ja toimintamatka-arvion, ja alareunassa on neljä isoa käskyä: lataa nyt, estä lataus, 80 % aamuksi ja 100 % aamuksi. ”Latausvalmius päällä” on tässä tarkoituksella eri asia kuin ”lataa” — rele voi olla kytkettynä ilman että auto ottaa tehoa, ja tämä sarja on jo kertaalleen käsitellyt sen, miksi näiden erottaminen on tärkeää. Sivun yläkulmassa lukee, kulkeeko ohjaus final gaten kautta.

Lämpö-sivu kokoaa lämpövyöhykkeet, sisä- ja ulkolämmön, sisälämpötilatavoitteen säädön ja käyttöveden. Tavoitteen säätö on 18–23 °C yhden asteen askelin, ja sen alla lukee rehellisesti järjestelmän tila: ”Tavoiteohjaus estyy: control_mode_shadow”. Sisälämpötavoitteen fyysinen vaikutus on nimittäin edelleen shadow-tilassa — koko ketju käyttöliittymästä kanoniseen tilaan toimii, mutta comfort wheeliin ei vielä kosketa, koska sisälämpötila on hidas prosessi jota ei voi validoida heinäkuisella minuuttitestillä. Paneeli näyttää tämän tilan suoraan sen sijaan että teeskentelisi ohjaavansa.

Energia-sivu on päivän spot-hintakäyrä, nykyiset päätökset domaineittain ja aktiivisen charge_by-tilauksen varatut vartit. Se on read-only. Tässä on rehellinen puute, jonka paneeli näyttää sellaisenaan: normaalin automaation yleistä 96 vartin EV-suunnitelmaa backend ei vielä julkaise, joten sivulla lukee ”current decision only” silloin kun aktiivista aikataulutettua latausta ei ole. Paneeli ei keksi suunnitelmaa jota backend ei anna.

Kotona ja Poissa: käyttötila, jolla on omistaja
Uusin toiminnallinen lisä on käyttötila. Koti-sivun yläosassa on kaksiasentoinen valinta: Kotona tai Poissa. Se näyttää yksinkertaiselta kytkimeltä, mutta sen alla on sama omistajuusajattelu kuin kaikessa muussakin.
Poissa ei ole löyhä ”säästötila”. Se julkaisee tunnistettavan 24 tunnin charge_block-pyynnön ja uusii sen tunnin välein — eli sammuttaa sähköauton latauksen ja pitää sen sammuksissa niin kauan kuin tila on Poissa. Kotona taas palauttaa normaalin automatiikan poistamalla vain käyttötilan itsensä tekemän eston. Tämä rajaus on tärkeä: Kotona ei saa tyhjentää käyttäjän erikseen tekemää muuta käsiohjausta. Jos olet käsin estänyt latauksen jostain omasta syystäsi, Kotona-nappi ei kumoa sitä — se koskee vain omaa estoaan. Jokaisella estolla on oma source ja request_id, ja tila purkaa vain sen mitä se itse omistaa.
Live-testissä tämä toimi juuri niin: Poissa sammutti EV-releen alle minuutissa, ja Kotona antoi releen kytkeytyä heti takaisin, jos normaali automaatio pyysi latausta.
Käyttötila v1:llä on tietoinen rajaus, jonka paneeli ei piilota: se vaikuttaa toistaiseksi vain sähköauton lataukseen. Jos Poissa syrjäyttää aktiivisen 80/100 % aamuksi -tilauksen, tilausta ei vielä palauteta automaattisesti Kotona-tilaan palattaessa. Paluuaika sekä lämmitys- ja käyttövesivaikutukset ovat jatkokehitystä — eikä niitä kannata tehdä ennen kuin on määritelty tarkasti, mitä Poissa oikeasti muuttaa kussakin domainissa ja kuka sen omistaa.
Utility meter, joka näytti järkevää nollaa
Yksi VP4.2:n konkreettisista korjauksista ansaitsee oman kappaleensa, koska se on juuri sitä hiljaisten vikojen luokkaa, josta tämä sarja on kirjoittanut aiemminkin. Päivän energialukemat — verkosta ja verkkoon tänään, eli pörssisähkön ja aurinkosähkön tase — nojasivat aiemmin Home Assistantin utility meter -antureihin, jotka jäivät keskiyön nollaan eivätkä seuranneet varsinaisia P1-mittarilähteitä. Ne näyttivät järkevää nollaa. Nolla on uskottava luku, ja juuri siksi vika oli hankala huomata: mittari näytti toimivalta, koska sen arvo oli täysin mahdollinen.
Vika paljastui vasta seuraavan vuorokauden aikana: lukema ei kertynyt, vaikka verkosta selvästi otettiin ja verkkoon syötettiin. Korjaus vaihtoi lähteeksi kumulatiiviset HomeWizard-mittarit, jotka eivät nollaudu jaksoittain ja ovat aina saatavilla; ne kalibroitiin korjauspäivän toteutuneisiin arvoihin, ja Voimapirtti siirrettiin lukemaan niitä. Todennus oli karkea mutta kiistaton: lähteen 7 kWh:n muutoksen piti näkyä mittarin 7 kWh:n muutoksena — ja näkyi. Utility meter voi siis näyttää järkevää nollaa, vaikka data ei kerry. Siksi lähteen ja johdetun anturin päivitystapahtumat on verrattava keskenään, ei tuijotettava pelkkää lopputulosta.
Muut opit tien varrelta
Käyttöliittymän rakentaminen tuotti oman kokoelmansa hiljaisia sudenkuoppia, jotka kannattaa nimetä:
CSS-luokkanimen törmäys voi piilottaa koko dokumentin. Yksi väärä luokkanimi teki ruudusta valkoisen — ei virheilmoitusta, ei mitään, pelkkä tyhjä valkoinen näyttö. Virheansan pitää näyttää syy valkoisen ruudun sijaan, muuten vika on mahdoton paikantaa seinällä olevalta tabletilta.
Android WebView voi olla vanhempi kuin kehityskoneen selain. Sivu, joka toimii moitteetta läppärillä, voi kaatua tabletin selainmoottorissa vanhempaan JavaScript-ominaisuuteen. Yhteensopivuus on testattava sillä laitteella, jolla paneeli oikeasti pyörii, ei sillä jolla se kehitetään.
Home Assistantin YAML-avain, friendly name, unique_id ja entity_id ovat eri asioita. Sensorin oikea runtime-nimi ei ole se, miltä se YAML:ssa näyttää — se pitää tarkistaa entity registrystä tai recorderista.
Puuttuva current-state-node ei saa jättää HTTP-pyyntöä auki. Telemetria-adapteri tarvitsee timeoutin ja fallback-vastauksen, muuten yksi puuttuva solmu jättää selaimen odottamaan ikuisesti.
Opit
Käyttöliittymän tärkein työ oli kääntäminen, ei koristelu. Tekninen paneeli näytti järjestelmän tilan sellaisenaan. Voimapirtti kääntää sen ihmisen kielelle — ”Latausvalmius päällä”, ”Halpa/aurinko”, ”Poissa · EV OFF” — ilman että se piilottaa mitään olennaista. Vaikein osa ei ollut fonttien ja värien valinta vaan sen päättäminen, mikä tila ansaitsee mitäkin sanan.
Esityskerros ei saa omistaa logiikkaa. Kun paneeli ei laske hintoja, sulakerajoja eikä ohjaa laitteita, se ei voi myöskään erehtyä niissä. Sama rajapinta, joka syöttää tabletille tilan, voidaan antaa mille tahansa muulle esittäjälle ilman että kukaan saa laiteoikeuksia.
HTTP 200 ei ole komentovahvistus. Aktiivinen tai valmis tila todennetaan request_id:n ja kanonisen statuksen kautta. Tämä periaate maksoi itsensä takaisin jokaisessa napissa: käyttäjä näkee vahvistetun tilan, ei sitä että pyyntö lähti.
Puuttuva arvo on viiva, ei nolla. Nolla on oikea mittausarvo, ja sen esittäminen puuttuvan datan tilalla on valhe, jonka varassa käyttäjä tekee päätöksiä. Lämpö-sivun tyhjät huonerivit ovat juuri nyt tämän periaatteen elävä todiste.
Kaksi rinnakkaista kehitystyötä eivät saa muokata samaa flows.json-tiedostoa keskeneräisinä. Voimapirtti lukittiin tuotantoon omaksi committikseen, ja sen rinnalla kulkeva telemetriatyö rakennetaan tuon lukitun HEADin päälle — ei samaan installeriin, backupiin eikä committiin. Juuri tästä syystä Lämpö-sivun anturit ovat tänään hiljaa: toinen työ omistaa ne hetken, ja raja pidetään puhtaana tarkoituksella. Looginen työ commitoidaan ensin, ja seuraava asennus sidotaan uuteen HEADiin.
Mitä vielä puuttuu
Rehellisyyden nimissä lista on tälläkin kertaa pitkä. Käyttötilan ja uusittavan EV-eston restart-regressio on vielä ajettava läpi: Lenovon, Node-REDin, Home Assistantin ja tabletin pitää selvitä uudelleenkäynnistyksestä niin, että Kotona/Poissa ja eston uusiutuminen palautuvat oikein. Offline-regressio — Wi-Fi poikki ja takaisin — pitää todentaa niin, että UI näyttää OFFLINEn, lukitsee komennot ja palautuu itsestään. Normaalin EV-automaation 96 vartin suunnitelma puuttuu Energia-sivulta, samoin PV-lähteen yhtenäinen tuoreustieto. Poissa-tilan paluuaika, laajemmat käyttötilat (vieras, yö, pitkä poissaolo) ja käyttötilan vaikutus lämmitykseen ja käyttöveteen ovat kaikki jatkokehitystä, joka odottaa selkeitä omistajuussääntöjä. Ja se F17-siirtymäluokitus: EV-eston purkamisen jälkeen resolveri voi hetkellisesti kirjata decided_no_relay -poikkeaman, vaikka paluu automatiikalle toimii — normaali rele/readback-siirtymä pitää osata erottaa todellisesta häiriöstä, ettei paneeli näytä turhaa järjestelmävirhettä.
Mutta nykytila kelpaa tuotantoon: Voimapirtti pyörii seinällä, sähköauton ja käyttöveden käsiohjaus, sisälämpötavoitteen asetus ja Kotona/Poissa toimivat päästä päähän, päivän energiat kertyvät oikein — ja tabletilla ei edelleenkään ole oikeuksia yhteenkään releeseen.
Seuraavassa osassa mennään sen taakse, mitä Voimapirtin ”80 % aamuksi” -nappi todella käynnistää. Määräaikaan sidottu lataus näyttää napilta, mutta sen takana on suunnitelma, joka lukee lähtöajan, varaustason ja huomisen varttihinnat ja rakentaa niistä latauksen halvimmille varteille — ilman että se lupaa liikaa niistä prosenteista, joita auton oma latauselektroniikka hidastaa loppua kohden. Siitä, ja siitä miksi auto täyteen aamukuudeksi on hankalampi lupaus kuin miltä se kuulostaa, kertoo osa 21.

















