2024. június 16., vasárnap

Gyorskeresés

Okos termosztátból ténylegesen okos termosztátot!

Írta: | Kulcsszavak: okosotthon . termosztát . iot . linux . prometheus . smarthome . python

[ ÚJ BEJEGYZÉS ]

Történetünk 2022-ben kezdődött, amikor is elromlott a régi RF-es Computherm termosztát és új delikvens után kellett nézni. Korábban bevált a hordozható RF-es termosztát, mivel ha az egyik szobában hidegebb van, mint a többiben, akkor simán el lehet cipelni oda a készüléket, ami aztán gondoskodik arról, hogy addig fűtsön, amíg el nem éri a helység a kívánt hőmérsékletet. Nem úgy, mint a falra szerelt fix termosztátoknál, amik jobb esetben az adott szoba hőmérsékletét monitorozzák csak.

Új delikvens után pásztázva a neten belefutottunk egy új termosztátba, ami a hordozható RF mellett WiFi támogatással is rendelkezik. Azt ígérte, hogy akár távolról is vezérelhetem majd az otthoni hőmérsékletet. Mivel ugye haladni kell a korral, meg is lett rendelve, be is lett szerelve.

Egy kicsit macera volt a kezdeti párosítás a WiFi hálózattal, ugyanis csak 2.4 GHz-et támogat, illetve WPA3-at nem, de párosítás után látszik, hogy nem árultak zsákbamacskát. Pontosan azt teszi, amit ígért. Megy a mobilapp, amivel lehet itthonról és távolról is felügyelni a fűtést, le lehet olvasni, hogy mennyi épp a szobában a hőmérséklet, mennyi van beállítva, meg lehet látni a fűtés állapotát.

Viszont kicsit a weboldaluk megtévesztett a vásárlás során, a sok megfelelőségi plecsni miatt azt véltem elsőre feltételezni, hogy ha nem is magyar, legalább egy európai termékről van szó és nem egy átcímkézett, tömegtermelt kínai IoT eszközről van szó. Hamar rájöttem, hogy nem, sajnos ez is kínai, viszont ugye EU-s előírásoknak mindenképp meg kell felelni, ha itt akarják eladni, tehát nem kéne olyan nagyon aggódnom mégsem.

Mégsem tudtam nyugodtan aludni, mert az IoT eszközök többségéről köztudott, hogy nem a legerősebb a hardveres képességük. Egy termosztátba nem fognak mobil procit, meg OS-t rakni, mert drága lenne és sokat fogyasztana. Általában mikrokontrollerekkel oldják meg a dolgot, amiknek kicsi a számítási teljesítménye. Sokszor emiatt van az, hogy a gyártóknak saját protokollt kell tervezni, amin az eszköz kommunikál, és nem feltétlen lesz olyan biztonságos, mint kéne neki. Elég sok IoT eszközről olvastam már sérülékenységeket, de a leghíresebb talán a Mirai botnet volt.

Mivel az én új termosztátom is kommunikál egy távoli szerverrel annak érdekében, hogy távolról vezérelhető legyen, előfordulhat, hogy elkezd terjedni valami exploit, amit kihasználva a támadók átjáróházat csinálhatnak a belső hálózatomból.

Elhatároztam tehát, hogy nem feltétlen adok ennek a ketyerének szabad internet-hozzáférést, a távoli elérést megoldom magamnak, hiszen van itthon okosabb eszköz (pl. router), ami mindenképp fut 0-24, szóval meg tudja oldani a biztonságos adatcserét úgy, ahogy én szeretném. Berakom egy izolált iot VLAN-ra az eszközt, aztán ott max a routerrel fog tudni beszélgetni, semmi mással.

Másodlagos célom a paranoia mellett egy használhatóbb kezelőfelület lett volna, mert valahogy a gyárilag adott app nem igazán nyerte el a tetszésem kezelhetőség szempontjából, hogy őszinte legyek (bocs a nagy képért, de nem szeretném a PH-ra feltölteni, nehogy copyrightot sértsek):

Oké, de fogalmam nem volt 2022-ben, hogy hogyan szólítsam meg a termosztátot kézzel. A mobilapp nyilván tud vele kommunikálni, de nem mertem visszafejteni, nehogy véletlen valami jogsértőt kövessek el más szellemi tulajdonában. Maradt tehát, hogy elindítottam egy Wiresharkot a hálózaton, aztán figyeltem, hogy milyen csomagok jönnek/mennek a termosztát felé. Arra lettem figyelmes, hogy UDP csomagokkal kommunikál igencsak gyakran (2-4 másodpercenként) az eszköz egy távoli szerverrel és a távoli szerver is kommunikál visszafele. A távoli szerver láthatóan német Amazon IP, a port 8900 és az IP-re rákeresve nem igen találtam semmi termosztátra utaló infót a Googleben.

Azt is kipróbáltam, hogy mi történik, ha elveszem a netet az eszköztől és csak a helyi hálózattal fog tudni kommunikálni. Szerencsére így is remekül működött, tehát az elképzelés, hogy izolálnám működhet!

Viszont az elküldött adatok elsőre csak értelmetlen zagyvaságnak tűntek. Feltételeztem, hogy (nagyon helyesen) titkosítva van valamilyen formában. Ráeresztettem egy entrópia ellenőrzést, amit mindig érdemes megtenni, ha titkosításra gyanakszunk. A wiki cikk nálam 1000x jobban el tudja magyarázni a dolog lényegét, de dióhéjban azt próbálja meg mérni az entrópia, hogy mennyi random generált adatot tartalmazhat az adat. Nálam okosabb crypto-s emberek azt szokták mondani, hogy általában minél több a véletlen faktor egy titkosítási eljárásban, annál nehezebb törni. Namost, én azt tapasztaltam, hogy esetünkben igen kicsi volt az entrópia. Alig változott pár byte a csomagokban. Emiatt ez nem lesz valami biztonságos, másrészt engem se nagyon segít ki a kutatás során, hogy mégis mi lehet ez.

Felfedeztem viszont, hogy a mobilapp a hálózaton elérhető eszközök felderítésére tud küldeni egy "discovery" csomagot, ami ugyan szintén titkosított, ez alapján talán szét tudtam volna nézni githubon, hogy hátha valami iot protokollnak van-e 48 byte méretű discovery csomagja. Illetve a kezdeti párosításnál is volt egy nagyobb, emlékeim szerint 130-140 byte körüli csomagja.

Szerencsére azonban nem kellett hosszan kutatómunkázni, mert Wiresharkban feltűnt, hogy a termosztát MAC címe BroadLinkTec_-ként lett felismerve. Bingó! Ezzel végre el lehet indulni.

Rákerestem Githubon, hogy hátha találok valami érdekeset. Beírtam, hogy broadlink és azonnal találtam is egy szimpatikusnak tűnő repót: [link]

A projekt leírása említ is sok más iot eszköz mellett támogatást termosztátra is, viszont ez a Hysen HY02B05H névre hallgat. Rákerestem erre is és az elém táruló termosztát elég hasonló volt a Computherm által forgalmazott másik termosztáthoz: [link]

Rákerestem arra is, hogy hátha van ennek is mobilalkalmazása. És igen, van! Méghozzá egy kínába szánt verzió (gondolom), meg egy direkt EU-ba szánt megoldás.

Még a mobilapp is hasonlít a Computhermesre. Sőt, fel is ismerte a Computherm termosztátot. :)

Más se kellett, visszatértem a Githubos projekthez, adtam fix IP címet a routeremben a termosztátnak, majd összekötöttem a routeremen futó VPN-nel a távoli szerverem. A setup tehát eddig:

Remek! Ideje a szerverről megpróbálni authentikálni:

>>> import broadlink
>>> device = broadlink.hello("192.168.1.8")
>>> device.auth()
True
>>> device.get_external_temp()
42.0
>>> device.get_temp()
24.5

Sajnos ez a get_temp és társai nincsenek nagyon ledokumentálva, de a forráskódot átnyálazva hamar megtaláltam.

Viszont ez elég para, hogy bárki rá tud csatlakozni a termosztátodra, ha ugyanazon a hálózaton van, mint te. Magyarul ha meghívsz valami leleményes hekkert, megadod neki az otthoni WiFi jelszavad, egyszer csatlakozik az eszközre, akkor utána a felhőn keresztül bárhonnan fogja tudni kapcsolgatni a termosztátod és megoldást nem találtam arra, hogy kidobjak eszközöket utólag. Szóval távolról felnyomhatja a hőmérsékletet 42 fokra, vagy épp szibériai -15 fokra, senki nem fogja észrevenni, csak mikor hazaér a ház tulaja...

De ezt majd mi megoldjuk, illetve az eredmény nekünk tök jó, mert visszaad valami hasznosat. Sőt, egy ilyet is:

>>> device.get_full_status()
{'remote_lock': 0, 'power': 1, 'active': 0, 'temp_manual': 0, 'room_temp': 24.5, 'thermostat_temp': 21.5, 'auto_mode': 0, 'loop_mode': 3, 'sensor': 0, 'osv': 42, 'dif': 2, 'svh': 35, 'svl': 5, 'room_temp_adj': 0.0, 'fre': 0, 'poweron': 1, 'unknown': 241, 'external_temp': 42.0, 'hour': 14, 'min': 31, 'sec': 25, 'dayofweek': 3, 'weekday': [{'start_hour': 6, 'start_minute': 0, 'temp': 21.0}, {'start_hour': 8, 'start_minute': 0, 'temp': 21.0}, {'start_hour': 11, 'start_minute': 30, 'temp': 21.0}, {'start_hour': 12, 'start_minute': 30, 'temp': 21.0}, {'start_hour': 17, 'start_minute': 0, 'temp': 21.0}, {'start_hour': 22, 'start_minute': 0, 'temp': 21.0}], 'weekend': [{'start_hour': 8, 'start_minute': 0, 'temp': 21.0}, {'start_hour': 23, 'start_minute': 0, 'temp': 21.0}]}

Rengeteg adat. Viszont a Hysen termosztát, amire a library eredetileg készült nem olyan okos, mint a Computhermes változat, ugyanis mért hőmérsékletnek most 24.5 fokot mutat, pedig 24.6 fok van. A termosztát ezt is mutatja... A Hysen csak X.0 és X.5 hőmérsékletet képes küldeni, míg a Computhermes termosztát tudja az összes digitet mérni egy tizedesjegyig. Szóval ha 21.1 fok van, akkor az 21.1 lesz és nem 21.0 az appban is.

Gondoltam, hogy bizonyára csak pár extra byte/integer/akármi jöhet a termosztáttól, aminek nem ismerem a jelentését, szóval jóhiszeműen ráírtam a forgalmazóra, hogy nem-e adná ki a protokoll doksiját, vagy bármit, ami segíthetne. Udvarias, de egyértelmű elutasítás volt a válasz, illetve az, hogy van egy másik magyar cég, aki megvalósította a támogatást és vegyem meg azt. A gond csak az, hogy a cégnek jobban utánanézve, az open source HomeAssistantot fogták és árulják pénzért, supportért és néhány extra funkciót adtak még, mint ez a Computherm támogatás gondolom. Nyilván rengeteg ilyen cég van, ami open source mellé ad supportot, de én nem szívesen támogatok ilyesmit, mert feltételezem, hogy a HA eredeti fejlesztői egy vasat nem látnak vissza abból, ha én ilyen módon veszek támogatást, másrészt nincs szükségem supportra, mert meg tudom oldani magamnak. Harmadrészt nem csak home assistantban szeretném használni az adatokat.

Szóval maradt az, hogy edukáltan tippelgetünk. Szerencsére az eszközzel már tudok kommunikálni, sót a hőmérséklet adatok bekérése is valamilyen szinten le van implementálva. Arra gondoltam, hogy a device.get_full_status() hívás látszólag mindent is visszaad az eszközről, tehát ebben biztosan megtalálom majd a hiányzó adatot. Kikerestem a forráskód ide vonatkozó részét és megtaláltam ezt:

def get_full_status(self) -> dict:
"""Return the state of the device.

Timer schedule included.
"""
payload = self.send_request([0x01, 0x03, 0x00, 0x00, 0x00, 0x16])
data = {}
data["remote_lock"] = payload[3] & 1
data["power"] = payload[4] & 1
data["active"] = (payload[4] >> 4) & 1
data["temp_manual"] = (payload[4] >> 6) & 1
data["heating_cooling"] = (payload[4] >> 7) & 1

A kód persze folytatódik, de a lényeg látszik ennyiből is. Küldünk egy nyers kérést, majd a kapott nyers adatot feldolgozzuk, ami igazából csak bizonyos byteok stb kibontása az adatból. Sokáig bámultam a nyers adatot, ábrázoltam, rohangáltam a szobák között a termosztáttal, mire sikerült rájönni, de úgy tűnik, hogy a következő algoritmus lett végül a befutó:

def get_temp() -> float:
"""
Returns the current measured temperature
"""
payload = device.send_request(bytearray([0x01, 0x03, 0x00, 0x00, 0x00, 0x16]))
b5 = payload[5] & 255
b6 = payload[18] & 255
b7 = payload[8]
i = (payload[4] >> 5) & 1
i2 = (payload[4] >> 3) & 1
i3 = ((payload[17] >> 4) & 1) + (((payload[17] >> 5) & 1) * 2)

def calculate_temperature(value, i2, i3):
base_temp = int(value / 2)
if value % 2 != 0:
if i2 != 0:
decimal = 6 + i3
else:
decimal = 5
else:
if i2 != 0:
decimal = 1 + i3
else:
decimal = 0
return f"{base_temp}.{decimal}"

room_temp = calculate_temperature(b5, i2, i3)
external_temp = calculate_temperature(b6, i2, i3)

if b7 == 0:
set_temp = room_temp
elif b7 == 1 and i == 1:
set_temp = external_temp
elif b7 == 2:
if i == 1:
set_temp = "0.0" if b5 == 0 else room_temp
else:
set_temp = "0.0" if b6 == 0 else external_temp

return float(set_temp)

A trükk az, hogy a hőmérsékletet el kell osztanunk kettővel, mivel minden esetben a ténylegesen mért érték duplája szerepel a protokollban. Ez lesz fixen a tizedesjegy előtti szám, azaz a hőmérséklet egész része. Majd meg kell nézni, hogy az eredeti érték páratlan-e. Ebben az esetben a régi algoritmus, a Hysennél mindenképp .5-öt írt végződésnek, viszont Computhermnél elérhető az általam i2-nek nevezett változó, ami megmondja, hogy elérhető-e részletesebben a tizedesjegy infó (illetve 0 lesz az értéke akkor is, ha kereken 5 az érték). Ha ez egy, akkor tudjuk, hogy elérhető, szóval ilyenkor az i3-at szimplán hozzáadjuk és tudjuk, hogy 5-nél biztos nagyobb a tizedesrész, szóval 6 + i3 lesz a kapott helyes tizedesjegy. Ha pedig páros, akkor ennek a pepitája történik, csak tudjuk, hogy .5 alatt van az érték. Ezeket levezetve sikerült megkapnom a korrekt értéket.

Szerettem volna ábrázolni az eredményt, hogy tudjam hosszútávon monitorozni a hőmérsékleteket, amihez telepítettem a Prometheus névre hallgató, kifejezetten metrikák tárolására optimalizált adatbázist, illetve a Grafana webes frontendet hozzá, majd összedobtam gyorsan egy dashboardot, illetve írtam egy kezdetleges exportert, ami a kapott hőmérsékleti adatokat prometheus által emészthető formában kirakja az internetre. Ehhez őt használtam.

Aztán megcsináltam a Google Assistant támogatást is, mert bár megvetem a Google-t, érdekelt, hogy hogy lehetséges hasonlót csinálni. Ehhez a következő könyvtárat találtam: [link]

A következő szkripttel álltam elő hozzá: [link] Nem a legoptimálisabb, mert többször kéri le az összes adatot, de kipróbálni jó volt ezzel is.

Nagyon kényelmes hangvezérelve, bárhonnan lekérdezni a hőmérsékletet, illetve ki-bekapcsolni a fűtést, azt meg kell hagynom a Googlenek.

Illetve csinálgattam olyan projekteket is, hogy wifin nézem, hány mobileszköz van felcsatlakozva, és ha nincs itthon egy mobil sem, akkor feleslegesen menne a fűtés, ilyenkor automatikusan kikapcsol. Készítettem egy appot, amivel biztonságosan tudom kapcsolgatni a fűtést, illetve csináltam egy machine learning modellt, ami próbálja megjósolni a hó végi fogyasztást. Ezeknek annyira nem mennék bele a részleteibe, mert a wifis megoldás eszköz függő, ha nem ugyanolyan routered van, mint nekem, akkor haszontalan számodra az infó. Az app, illetve a machine learning modell meg külön cikkek lehetnének.

De így végre szabadon kapcsolgathatom a fűtést bármilyen platformról és integrációval, nem hagyja el semmilyen adat az itthoni netet az eszközről és nem teszem ki annak a veszélynek az eszközt, hogy bárki kapcsolódjon rá a tudtom nélkül.

És hogy miért csak most publikálom az egészet, amikor már 2022-ben előállt a végleges állapot? Nos, féltem attól, hogy a protokoll visszafejtése - bár tudtommal a saját hálózatos felderítéssel teljesen legális - vitathatóan necces topik, féltem attól, hogy a forgalmazó/gyártó megkeres, és beperel. Anno ki is kértem a jogász topikban pár véleményt, ahol szintén azt írták, hogy ne tegyem meg. Azóta szerencsére ismerem a jogszabályt annyira, hogy tudjam, nem tettem semmi illegálisat. Illetve azért merem publikálni, mert alig pár hónapja valaki bemergelte az eredeti projektbe is a tizedespontok megfelelő támogatását: [link] Szóval már kézi hekkelés sem szükséges, a legfrissebb lib verzió megoldja ezt. Igaz, a Githubos szál szerint a user az alkalmazás visszafejtésével találta ki az algoritmust, ami neccesebb megoldás, mint az enyém, de attól, hogy valaki használja ezt, nem lesz önmagában bűnöző.

De hátha valakinek érdekes lehet, hogy mennyi lehetőséget rejt egy okos termosztát, amit kedvünkre okosíthatunk. :)

Hozzászólások

(#1) Mr Dini


Mr Dini
addikt
LOGOUT blog

A topikot megnyitom! Lehet is ekézni, hogy milyen haszontalan dolgokra pazaroltam már megint az időm. ;]

Eleinte angol billentzuyetet akartam. De aztán megismerkedtem a nagy 'Ő'-vel!

(#2) Ceree


Ceree
senior tag

En is gondolkodtam ilyesmiben, de az elet meghozta magatol a valaszt.

Tortent ugyanis, hogy az egyik Home Assistant frissites utan a redonyok vezerlesere hasznalt Broadlink RF hub-on keresztul feldobta uj eszkozkent a computherm termosztatot. Fullra vezerelheto (es innentol nyilvan barmilyen masik trigger is megadhato), sot, history is lett igy, mind mert homersekletre, mind beallitott celertekre, mind mukodesi idore vonatkozoan.

Másolat_eredetije

(#3) Mr Dini válasza Ceree (#2) üzenetére


Mr Dini
addikt
LOGOUT blog

Ez tényleg menő. Amikor én setupoltam, 2022-ben még nem tudott ilyet gyárilag a HA. Azért kellett mindent kézzel megcsinálni.

Egyébként ténylegesen 21.0°C van a termosztáton is, vagy ez is rosszul kerekít még? Grafikon alapján ha jól látom, még ez is csak a .5, .0 értékeket közli, nem pedig minden tizedesjegyet pontosan. Persze részletkérdés, de ha egyszer tudja az eszköz... :D

Illetve az auto mód mit tud? Akkor kapcsol fel, ha a megadott hőmérséklet alá esik a mért hőmérséklet? A fűtés pedig fixen bekapcsolja?

[ Szerkesztve ]

Eleinte angol billentzuyetet akartam. De aztán megismerkedtem a nagy 'Ő'-vel!

(#4) Ceree válasza Mr Dini (#3) üzenetére


Ceree
senior tag

Igen, pontosan.

De epitheto lenne ra automatizmus is, minden szobaban van egy xiaomi homero, az ket tizedesre mer, a radiatorszelep meg lekapcsolna a futotestet, ha mar rendben van a helyiseg. (De persze oda is lehetne okos termoszelep).

Azert nem hasznalom megse igy, mert egyelore egy koron vannak a szobak meg a padlofuteses kor is, az pedig teljesen mas hiszterezissel megy. Tervben volt a levalasztas, megrendeltuk a mestertol osszel, de nem ert ide... Sajnos nem mentem utana, majd iden talan.

Másolat_eredetije

(#5) Krugszvele válasza Mr Dini (#1) üzenetére


Krugszvele
senior tag

Micsoda?? De hát ez hekkerkedés! A legmagasabb szintű tevékenység, amit szabadidejében végezhet az ember! Hatalmas gratuláció, respect!
Pontosan erre való az internet, az IoT meg az összes többi varázsszó: játékra, szórakozásra! És ahogy elszeparáltad, az is tanítanivaló! Csak így tovább!

(#6) hcl


hcl
félisten
LOGOUT blog

Szép munka! Ilyenek miatt tartok én is a mindenféle középgagyi okoscuccoktól, mint amit te fejtegettél.

Mutogatni való hater díszpinty

További hozzászólások megtekintése...
Copyright © 2000-2024 PROHARDVER Informatikai Kft.