2022 gehe ich langsam davon aus, dass IOT Geräte, die man so kauft eine einigermaßen gut zugängliche API haben um sie auch ohne die hauseigenen Apps zu bedienen zu können.
Leider stand ich vor dem Problem meine neue Heizungssteuerung von Fenix, die „Fenix TFT Wifi“ nur über die herstellereigene App bedienen zu können.
Eine smarte Heizungssteuerung soll auf Umwelteinflüsse, wie die Wetterprognose hören, soll darauf reagieren, wenn solare Überkapazität im Haus verfügbar ist oder wenn die Bewohner sich gerade 200km weit entfernt aufhalten. 2022 will ich mich im Alltag nicht mehr mit einer Heizung beschäftigen müssen. Das soll sich bitte selbst regeln.
Ich schaue mir die Software meines Heizungsthermostates einmal genauer an und zeige Euch hier mein Ergebnis wie Ihr Euer eigenes Fenix TFT Thermostat auslesen und die Temperatur ändern könnt. Es ist leider kein einfaches Copy&Paste, das tut mir leid aber ich hoffe ich erspare Euch hiermit das ganze Reverse Engineering an dem ein Freund von mir und ich recht lange saßen. Seht es eher als kleinen Workshop und über Feedback und Mitarbeit an dem Flow freue ich mich natürlich.
Was ihr dazu braucht:
- Node-Red
- Homeassistant
- Die App Proxyman oder etwas äquivalentes
Ganz unten findet Ihr den Node-Red Export, den Ihr Euch importieren könnt.
Kleines Update:
Wir verwenden in dem Flow eine Funktion, die seit einiger Zeit nicht mehr maintained wird, die aber essentiell ist für die Funktion. Leider hat Node Red entschieden das Paket nicht mehr zu supporten. Ihr müsst also das Paket node-red-contrib-http-request manuel in Node Red importieren
Geht dazu im Burgermenü auf Palette verwalten und sucht nach dem Paket und installiert es manuel
Los gehts. Ich beziehe mich auf die Zahlen vor den Node-Red Einträgen.
JWT Token auslesen
Nur mit dem JWT Token kommt ihr an die Daten von dem Thermostat heran. Den Token hole ich mit der Inject Node, die regelmäßig feuert ab und schreibe den Token in den Node Red eigenen Context store. Daraus bedient sich am Ende der Prozess zum auslesen der Daten und zum Schreiben der SOLL Temperaturwerte.
Mit Proxyman schneidet ihr erst einmal einen Loginprozess mit. Davor die App einmal abmelden und schließen. Jetzt lasst ihr Proxyman mitlaufen und hört was passiert.
Alle Proxyman Einträge, die irgendwas hiermit zutun haben könnt ihr der Übersicht halber löschen
- *.css
- *.js
- consent.google.com
- *visualstudio*
- *in.appcenter.ms*
- *.google.com*
Übrig bleiben sollte ungefähr sowas:
NR Punkt 1
Vermutlich müsst ihr hier nichts ändern, sofern sich die Abfragestruktur nicht geändert hat aber beim NR Punkt 1 kommt die URL rein von meinem Proxyman Nr.93. Das ist der letzte Redirect der gemacht wird und schaut ungefähr so aus:
https://vs2-fe-identity-prod.azurewebsites.net/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Db1760b2e-69f1-4e89-8233-5840a9accdf8%26client_secret%.......
Das ist das Loginformular und diese Seite könnt ihr auch ohne die App im Browser öffnen 🙂 Das Formular ist wichtig wegen dem „Request Verification Token“. Damit keine Bots das Formular ausfüllen, Lol!
NR Punkt 2
Löscht der Übersicht halber den Proxyman nochmal und füllt das Formular in der App aus und hört wieder mit. Jetzt müssen wir die Formularfelder mitschneiden. Der erste POST der jetzt gemacht wird enthält im Body die ReturnURL. Wir kopieren den kompletten Body wo weiter unten auch unsere Mailadresse und das Passwort drin stehen heraus aber ACHTUNG! Ganz am Ende findet ihr wieder den „RequestVerificationToken“. Den Teil hinter dem „=“ wollen wir nicht statisch mitnehmen. Unser Script wird das als Variable anhängen.
Also den kompletten „ReturnURL=“ aber ohne das tatsächliche Token in das „CHANGEME“ bei der „msg.payload“ einfügen. Das ist tatsächlich der „Geheime“ Teil wo die Anmeldedaten drinstehen. Das Request Verification Token wird als Variable angehängt.
NR Punkt 8
Prüft am Ende hinter der Debug Node 8, ob ihr den JWT Token re-regext bekommt.
NR Punkt 9
Prüft, ob der Token in den Context Store von Eurem NR eingefügt wurde. Dieser beginnt immer mit „eyJ…“
Das Token ist die Grundlage für die weiteren Schritte. Wenn Ihr den Token bekommen habt: Glückwunsch, der schwierigste Teil ist geschafft 🙂
Temperatur auslesen
Jetzt brauchen wir noch die Device ID von unserem/unseren Fenixen und müssen dem Backend sagen, dass wir da sind und Werte haben wollen. Wenn Ihr in der Fenix App angemeldet seid, löscht nochmal den Proxyman, startet die Aufzeichnung und drückt den Reload Button in der Fenix App um Euch neue, aktuellere Daten abzuholen.
NR 1 „user connected“
Ich glaube das ist ein Bug in der App aber der letzte Request der hier gemacht wird sollte eigentlich der erste sein. Hiermit sagen wir nämlich dem Fenix Cloud Backend, dass wir „da sind“ und neue Werte haben wollen. Es kann aber auch Absicht sein, dass der User beim Einloggen schneller Werte sehen kann (die aber alt sind). Aktuelle Werte bekommt man nämlich erst nach diesem Request. Es geht um die ID 1013 in meinem Bsp. Wir brauchen den kompletten Body, den wir (als RAW) kopieren und in msg.payload einsetzen.
Schaut Euch den Response von dem Request einmal genauer an (drückt gerne ein paar Mal Reload, wenn nichts zurück kommt. Manchmal bekommt man hier nichts zurück weil das Backend etwas buggy ist. Hier bekommt ihr die Geräte ID bzw. Sensor ID von den/dem Fenixen zurück. Bei mir sind es zwei, bei Euch können es auch mehr oder weniger sein. Diese ID(s) merken wir uns ebenfalls.
NR 3 „read Fenix“
Immer wenn wir im angemeldeten Zustand reload bei einem der Geräte drücken (oder in der Übersicht) sieht man einen Request an /v1/configuration und dann mit einer GeräteID und SensorID (die scheinbar die gleiche ist). Das ist auch die ID von NR1 „user connected“.
Bei mir sind es wieder zwei, weil zwei Geräte.
Ihr passt jetzt in NR3 „read Fenix“ die msg.url entsprechend an und tragt hier zwei mal die GeräteID/SensorID ein.
Die restlichen Nodes machen noch Weiterleitungen, holen die Daten an einem anderen Endpunkt ab, holen sich die Temperaturwerte der Geräte aus der Rückgabe usw. Ich verstehe den Aufwand nicht. Aber am Ende habt ihr hoffentlich die Raumtemperatur und die Settemperatur. In meinem Beispiel schreibe ich die Daten noch in dafür vorbereitete Variablen weg, dass ich sie im Frontend gleich anzeigen lassen kann.
Setze eine neue Temperatur
Jetzt wollen wir noch die Temperatur setzen, die wir gerne hätten. Sorry, ich bin noch nicht so weit hier einen „schönen“ Flow zu präsentieren. Aber mit dem Snippet hier solltet ihr wenigstens loslaufen können.
NR 1 Inject
Im Payload der Inject Node steht der Body, den ihr auch beim Ändern der Temperatur in der App beobachten könnt, wenn ihr mit Proxyman mithört. Was ihr hier ändern müsst ist die
Device ID: „Id_deviceId“
und die
Sensor ID: „S1“ (Die sind beide gleich bei mir)
Die Temperatur, die ihr setzen wollt ist die „MA“ wattsTypeValue „. In dem Fall „689“ hierbei handelt es sich um 68.9°F, also 20,5°C. Ihr müsst die Temperatur also erstmal in °F umwandeln und ohne Nachkommastelle mit 10 multiplizieren. Diesen Wert könnt ihr dann wegschicken.
Was die „Dm = 6“ tut habe ich leider nicht rausgefunden aber ich schätze es ist der Modus in dem das Thermostat laufen soll. Die „6“ könnte der manuelle Modus sein.
Triggert die Node und schaut, ob Eure Temperatur sich geändert hat. Wenn ja: Glückwunsch!
Fazit
Respekt, wenn ihr es bis hier geschafft habt und sorry, dass ich es nicht besser aufbereiten konnte. Der Prozess ist einfach hacky ohne Ende und ich habe beim Erstellen auch gekotzt weil vieles für mich einfach nicht nachvollziehbar war. Ich bin aber auch kein Profi in dem Bereich. Das ist das erste Mal, dass ich mit APIs roh sprechen muss. Auch, dass eine wichtige Funktion, wie z.B. das „user connected“ in der App ganz am Ende gemacht wird und aber essentiell ist um aktuelle Daten zu bekommen hat es nicht einfacher gemacht.
Eine Bitte zu Schluss
Wenn Ihr das besser könnt (den Prozess, die Doku usw.) meldet Euch bitte bei mir. Ich würde das gerne möglichst vielen Leuten ermöglichen ihre Fenix Thermostate auszulesen. Zum aktuellen Zeitpunkt gibt es hierzu nämlich gar nichts. Schreibt das um für andere Systeme, dokumentiert, übersetzt, schreibt die Firma Fenix oder Watts (die das vermutlich für Fenix gebaut haben) an und bittet um einen langlebigen Token mit dem man Roboter füttern kann oder/und eine richtige API. Das hier ist nur reverse engineered und vermutlich fehleranfällig und wenn die Fenix mal was an der API ändert sitzen wir wieder Nächte lang zusammen und versuchen das zu fixen. Ein Heimsteuersystem ohne lokale API, mit Cloudzwang und undokumentiert ist einfach ein Unding. Heimautomation lebt nicht von einer properitären schlechten App. Heimautomation reagiert auf externe Einflüsse, regelt sich selbst, hört auf jedes beliebige System von außen.
Der Node-Red Flow
[{"id":"8227d0add2657df9","type":"tab","label":"Thermostat Fenix Example","disabled":false,"info":"","env":[]},{"id":"79e3ce33d2d730fd","type":"inject","z":"8227d0add2657df9","name":"Inject every 10 minutes","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"600","crontab":"","once":true,"onceDelay":"60","topic":"","payload":"","payloadType":"date","x":190,"y":560,"wires":[["eba52c33bf2535eb"]]},{"id":"a46c6199e293ad83","type":"http request","z":"8227d0add2657df9","name":"","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":430,"y":860,"wires":[["fe7658b8f30516a8"]]},{"id":"fe7658b8f30516a8","type":"function","z":"8227d0add2657df9","name":"","func":"let ambient = msg.payload.types.find( item => item.wattsType == \"At\")\nlet setpoint = msg.payload.types.find( item => item.wattsType == \"Sp\")\n\n\nmsg = {}\n\nvar ambient_c = ((ambient.value /10 ) - 32) * 5 / 9;\nvar ambient_c = ambient_c.toFixed(2); // 2 decimals\n\nvar setpoint_c = ((setpoint.value /10 ) - 32) * 5 / 9;\nvar setpoint_c = setpoint_c.toFixed(2); // 2 decimals\n\nmsg.location = \"Wohnzimmer\"\nmsg.ambient_c = ambient_c;\nmsg.setpoint_c = setpoint_c;\n\nreturn msg;\n\n\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":460,"y":900,"wires":[["f5bd460a81ec381d","2cc5a06c953d0a62"]]},{"id":"0abf5c81067e9d95","type":"inject","z":"8227d0add2657df9","name":"Inject NR Start + 1h","props":[],"repeat":"3600","crontab":"","once":true,"onceDelay":"30","topic":"","x":140,"y":80,"wires":[["780df9407280a16d"]]},{"id":"780df9407280a16d","type":"http request","z":"8227d0add2657df9","name":"","method":"GET","ret":"txt","paytoqs":"ignore","url":"https://vs2-fe-identity-prod.azurewebsites.net/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Db1760b2e-69f1-4e89-8233-5840a9accdf8%26client_secret%3D76A55CCDD7C04848B2FCD4A17B540E814748FB05EE42706211CE7C52FF372A66%26response_type%3Dcode%2520id_token%26scope%3Dhttps%253A%252F%252Fvisionsystem2.com%252Foperations%252Fuser_impersonation%2520https%253A%252F%252Fvisionsystem2.com%252Fbusinessmodule%252Fuser_impersonation%2520https%253A%252F%252Fvisionsystem2.com%252Fdataprocessing%252Fuser_impersonation%2520https%253A%252F%252Fvisionsystem2.com%252Ffirmware%252Fuser_impersonation%2520https%253A%252F%252Fvisionsystem2.com%252Fiotmanagement%252Fuser_impersonation%2520https%253A%252F%252Fvisionsystem2.com%252Fschemas%252Fuser_impersonation%2520https%253A%252F%252Fvisionsystem2.com%252Ftou%252Fuser_impersonation%2520profile%2520openid%2520offline_access%26redirect_uri%3Dfenix%253A%252F%252Fcallback%26nonce%3D28eac9d903e8457b98bb13f2997d39fc%26code_challenge%3Dbo2lN-izi94_HwuI_1ggk79LYciCI_7VSmQltjtbrns%26code_challenge_method%3DS256%26state%3D3f2285e295f54c08a4dc3f55da76bcd5","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":190,"y":120,"wires":[["71a426ff24ccb6a0"]]},{"id":"817e703631c97bd5","type":"debug","z":"8227d0add2657df9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":670,"y":200,"wires":[]},{"id":"71a426ff24ccb6a0","type":"function","z":"8227d0add2657df9","name":"","func":"let cookie = msg.headers[\"set-cookie\"]\n\n\n\nvar parts = /__RequestVerificationToken\" type=\"hidden\" value=\"(?<token>.*)\"/;\nconst {groups: {token}} = parts.exec(msg.payload)\n\n\nmsg = {};\n\nmsg.method = \"POST\";\nmsg.url = \"https://vs2-fe-identity-prod.azurewebsites.net/Account/Login?ReturnUrl=/connect/authorize/callback?client_id=b1760b2e-69f1-4e89-8233-5840a9accdf8%26client_secret=76A55CCDD7C04848B2FCD4A17B540E814748FB05EE42706211CE7C52FF372A66%26response_type=code%20id_token%26scope=https%3A%2F%2Fvisionsystem2.com%2Foperations%2Fuser_impersonation%20https%3A%2F%2Fvisionsystem2.com%2Fbusinessmodule%2Fuser_impersonation%20https%3A%2F%2Fvisionsystem2.com%2Fdataprocessing%2Fuser_impersonation%20https%3A%2F%2Fvisionsystem2.com%2Ffirmware%2Fuser_impersonation%20https%3A%2F%2Fvisionsystem2.com%2Fiotmanagement%2Fuser_impersonation%20https%3A%2F%2Fvisionsystem2.com%2Fschemas%2Fuser_impersonation%20https%3A%2F%2Fvisionsystem2.com%2Ftou%2Fuser_impersonation%20profile%20openid%20offline_access%26redirect_uri=fenix%3A%2F%2Fcallback%26nonce=4a12316b271d4e308ecf3fa0d9ad0a0e%26code_challenge=oY4eRli8LhJ05Zp0H4Coq4aTIIof2q4dr7fBCXM_Dlk%26code_challenge_method=S256%26state=bbda076fc32e4f40b395462540c4d69c\";\nmsg.payload =\"CHANGEME\"+token;\n\nmsg.headers = {};\nmsg.headers[\"Host\"] = \"vs2-fe-identity-prod.azurewebsites.net\";\nmsg.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\nmsg.headers[\"Origin\"] = \"\";\nmsg.headers[\"Cookie\"] = cookie;\nmsg.headers[\"Accept\"] = \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\";\nmsg.headers[\"User-Agent\"] = \"Mozilla/5.0 (iPad; CPU OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148\";\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":220,"y":160,"wires":[["eb8a7df09b8241f1","a0c74cc5f607dc6d"]]},{"id":"eb8a7df09b8241f1","type":"http request","z":"8227d0add2657df9","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":230,"y":200,"wires":[["f3f596423b3a436f","a0c74cc5f607dc6d"]]},{"id":"f3f596423b3a436f","type":"function","z":"8227d0add2657df9","name":"","func":"msg.headers[\"Cookie\"] = [];\nmsg.headers[\"Cookie\"].push(\"idsrv.session=\"+msg.redirectList[0].cookies[\"idsrv.session\"].value+\"; Path=/; Secure;\")\nmsg.headers[\"Cookie\"].push(\".AspNetCore.Identity.Application=\"+msg.redirectList[0].cookies[\".AspNetCore.Identity.Application\"].value+\"; Path=/; Secure; HttpOnly;\")\n\n\nlet redirect = msg.redirectList[0].location;\n//msg = {};\n\nmsg.method = \"GET\";\nmsg.url = \"https://vs2-fe-identity-prod.azurewebsites.net\"+redirect;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":240,"wires":[["a03aa7e3e708cbf1","a0c74cc5f607dc6d"]]},{"id":"a03aa7e3e708cbf1","type":"www-request","z":"8227d0add2657df9","name":"","method":"use","ret":"txt","url":"","follow-redirects":false,"persistent-http":false,"tls":"","x":270,"y":280,"wires":[["73fb4217791dc70b","a0c74cc5f607dc6d"]]},{"id":"73fb4217791dc70b","type":"function","z":"8227d0add2657df9","name":"","func":"let cookie = msg.headers[\"set-cookie\"]\n\nvar parts = /callback#code=(?<token>.*)&id_token=/;\nconst {groups: {token}} = parts.exec(msg.headers.location)\n\n\nmsg = {};\n\nmsg.method = \"POST\";\nmsg.url = \"https://vs2-fe-identity-prod.azurewebsites.net/connect/token\";\nmsg.payload =\"grant_type=authorization_code&code=\"+token+\"&redirect_uri=fenix%3A%2F%2Fcallback&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk\";\n\nmsg.headers = {};\n\nmsg.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n\nmsg.headers[\"Cookie\"] = cookie;\nmsg.headers[\"Accept\"] = \"application/json\";\nmsg.headers[\"User-Agent\"] = \"Watts.VisionSystem.Mobile/2101281503 CFNetwork/1327.0.4 Darwin/21.2.0\";\nmsg.headers[\"Accept-Language\"] = \"de-de\";\n//msg.headers[\"Accept-Encoding\"] = \"gzip, deflate, br\";\nmsg.headers[\"Authorization\"] = \"Basic YjE3NjBiMmUtNjlmMS00ZTg5LTgyMzMtNTg0MGE5YWNjZGY4Ojc2QTU1Q0NERDdDMDQ4NDhCMkZDRDRBMTdCNTQwRTgxNDc0OEZCMDVFRTQyNzA2MjExQ0U3QzUyRkYzNzJBNjY=\";\nmsg.headers[\"Ocp-Apim-Subscription-Key\"] = \"73a25a3ade354b04a5431d60ce23024b,73a25a3ade354b04a5431d60ce23024b\";\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":320,"wires":[["35fb71ccbd39ceca","a0c74cc5f607dc6d"]]},{"id":"35fb71ccbd39ceca","type":"http request","z":"8227d0add2657df9","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":310,"y":360,"wires":[["da8e8feb178aa615","a0c74cc5f607dc6d"]]},{"id":"da8e8feb178aa615","type":"function","z":"8227d0add2657df9","name":"","func":"var parts = /access_token\":\"(?<token>.*)\",\"expires_in/;\nconst {groups: {token}} = parts.exec(msg.payload)\n\nmsg = {};\nmsg.token = token;\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":340,"y":400,"wires":[["7d4f91f82213a8e4","a0c74cc5f607dc6d"]]},{"id":"7d4f91f82213a8e4","type":"change","z":"8227d0add2657df9","name":"set JWTtoken","rules":[{"t":"set","p":"JWTtoken","pt":"flow","to":"token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":440,"wires":[["a0c74cc5f607dc6d"]]},{"id":"525569f81953469f","type":"function","z":"8227d0add2657df9","name":"Read Fenix","func":"\nmsg.method = \"GET\";\nmsg.url = \"https://vs2-fe-apim-prod.azure-api.net/iotmanagement/v1/configuration/AC67B253AE90/AC67B253AE90/v1.0\";\n\nmsg.headers = {};\nmsg.headers[\"Authorization\"] = \"Bearer \"+msg.token;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":740,"wires":[["9ab5798c268ccb7d"]]},{"id":"eba52c33bf2535eb","type":"change","z":"8227d0add2657df9","name":"Read JWT Token from the Context Flow Store","rules":[{"t":"set","p":"token","pt":"msg","to":"JWTtoken","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":280,"y":600,"wires":[["d984e66b53de21d2"]]},{"id":"9ab5798c268ccb7d","type":"http request","z":"8227d0add2657df9","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":370,"y":780,"wires":[["5de39fc5f13b846d"]]},{"id":"5de39fc5f13b846d","type":"function","z":"8227d0add2657df9","name":"","func":"var parts = /json\",\"url\":\"(?<token>.*)\\\"}/;\nconst {groups: {token}} = parts.exec(msg.payload)\n\n\nmsg = {}\n\nmsg.method = \"GET\";\nmsg.url = token;\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":820,"wires":[["a46c6199e293ad83"]]},{"id":"f5bd460a81ec381d","type":"api-call-service","z":"8227d0add2657df9","name":"","server":"86110a77.f6e448","version":3,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.fenix_wohnzimmer_ambient_temperature","data":"{\"value\":msg.ambient_c}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":700,"y":880,"wires":[[]]},{"id":"2cc5a06c953d0a62","type":"api-call-service","z":"8227d0add2657df9","name":"","server":"86110a77.f6e448","version":3,"debugenabled":false,"service_domain":"input_number","service":"set_value","entityId":"input_number.fenix_wohnzimmer_setpoint_temperature","data":"{\"value\":msg.setpoint_c}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":700,"y":920,"wires":[[]]},{"id":"a47fe63d8c58ee55","type":"comment","z":"8227d0add2657df9","name":"Read the temperature variables from Fenix and write them to the HA variables","info":"","x":330,"y":520,"wires":[]},{"id":"bcdf3de35644eb58","type":"comment","z":"8227d0add2657df9","name":"Periodically get a new JWT token and write it to the context store","info":"","x":250,"y":40,"wires":[]},{"id":"a0c74cc5f607dc6d","type":"change","z":"8227d0add2657df9","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":200,"wires":[[]]},{"id":"cd74f3a645950dfc","type":"http request","z":"8227d0add2657df9","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":330,"y":700,"wires":[["525569f81953469f"]]},{"id":"d984e66b53de21d2","type":"function","z":"8227d0add2657df9","name":"\"user connected\"","func":"\nmsg.method = \"PUT\";\nmsg.url = \"https://vs2-fe-apim-prod.azure-api.net/iotmanagement/v1/devices/userconnected\";\nmsg.payload ={\"A1\":\"XXXXX\",\"In\":\"XXXXXXX\",\"S1\":\"XXXXX\"};\n\nmsg.headers = {};\nmsg.headers[\"Authorization\"] = \"Bearer \"+msg.token;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":660,"wires":[["cd74f3a645950dfc"]]},{"id":"4f567402a510b5f6","type":"comment","z":"8227d0add2657df9","name":"1","info":"","x":50,"y":120,"wires":[]},{"id":"ab3ba204b7f28030","type":"comment","z":"8227d0add2657df9","name":"2","info":"","x":50,"y":160,"wires":[]},{"id":"e55bd38cf279b999","type":"comment","z":"8227d0add2657df9","name":"3","info":"","x":50,"y":200,"wires":[]},{"id":"bc17571d92500980","type":"comment","z":"8227d0add2657df9","name":"4","info":"","x":50,"y":240,"wires":[]},{"id":"646d5b654b8cd010","type":"comment","z":"8227d0add2657df9","name":"5","info":"","x":50,"y":280,"wires":[]},{"id":"47ec46cf37d74777","type":"comment","z":"8227d0add2657df9","name":"6","info":"","x":50,"y":320,"wires":[]},{"id":"5eddf14c5df563f0","type":"comment","z":"8227d0add2657df9","name":"7","info":"","x":50,"y":360,"wires":[]},{"id":"0e21babe7837c3b5","type":"comment","z":"8227d0add2657df9","name":"8","info":"","x":50,"y":400,"wires":[]},{"id":"407bc2ef9379f774","type":"comment","z":"8227d0add2657df9","name":"9","info":"","x":50,"y":440,"wires":[]},{"id":"ab40df272d4b63f2","type":"comment","z":"8227d0add2657df9","name":"1","info":"","x":50,"y":660,"wires":[]},{"id":"de91c1e7c21630ac","type":"comment","z":"8227d0add2657df9","name":"2","info":"","x":50,"y":700,"wires":[]},{"id":"67f34ac2f074b0d5","type":"comment","z":"8227d0add2657df9","name":"3","info":"","x":50,"y":740,"wires":[]},{"id":"7e59b7790bf456d2","type":"comment","z":"8227d0add2657df9","name":"4","info":"","x":50,"y":780,"wires":[]},{"id":"7cb660c36edc6a28","type":"comment","z":"8227d0add2657df9","name":"5","info":"","x":50,"y":820,"wires":[]},{"id":"5e348b3a0bee92a9","type":"comment","z":"8227d0add2657df9","name":"6","info":"","x":50,"y":860,"wires":[]},{"id":"084cec9fd64f0d4f","type":"comment","z":"8227d0add2657df9","name":"7","info":"","x":50,"y":900,"wires":[]},{"id":"47aa2fa790a683d8","type":"http request","z":"8227d0add2657df9","name":"","method":"use","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","senderr":false,"x":290,"y":1180,"wires":[["cfbbcbfe6c596298"]]},{"id":"8f2672fa77c8c2de","type":"inject","z":"8227d0add2657df9","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"Id_deviceId\":\"ACXXXXXX90\",\"S1\":\"ACXXXXXX90\",\"configurationVersion\":\"v1.0\",\"data\":[{\"wattsType\":\"Dm\",\"wattsTypeValue\":6},{\"wattsType\":\"Ma\",\"wattsTypeValue\":689}]}","payloadType":"json","x":210,"y":1060,"wires":[["a380ebf69687b19a"]]},{"id":"cfbbcbfe6c596298","type":"debug","z":"8227d0add2657df9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":310,"y":1220,"wires":[]},{"id":"a023a4558b859634","type":"function","z":"8227d0add2657df9","name":"","func":"msg.method = \"PUT\";\nmsg.url = \"https://vs2-fe-apim-prod.azure-api.net/iotmanagement/v1/devices/twin/properties/config/replace\";\nmsg.headers = {};\nmsg.headers[\"Authorization\"] = \"Bearer \"+msg.token;\n\n\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":1140,"wires":[["47aa2fa790a683d8"]]},{"id":"a380ebf69687b19a","type":"change","z":"8227d0add2657df9","name":"Read JWT Token from the Flow Store","rules":[{"t":"set","p":"token","pt":"msg","to":"JWTtoken","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":1100,"wires":[["a023a4558b859634"]]},{"id":"c3b8f770dfd54f7c","type":"comment","z":"8227d0add2657df9","name":"Set a temperature on a Fenix","info":"","x":210,"y":1020,"wires":[]},{"id":"67a1fa36d28c883b","type":"comment","z":"8227d0add2657df9","name":"1","info":"","x":50,"y":1060,"wires":[]},{"id":"5c120d85e70a9cfd","type":"comment","z":"8227d0add2657df9","name":"2","info":"","x":50,"y":1100,"wires":[]},{"id":"c29dca317db8a83b","type":"comment","z":"8227d0add2657df9","name":"3","info":"","x":50,"y":1140,"wires":[]},{"id":"846966753f00939c","type":"comment","z":"8227d0add2657df9","name":"4","info":"","x":50,"y":1180,"wires":[]},{"id":"0aa05765c7ebe755","type":"comment","z":"8227d0add2657df9","name":"5","info":"","x":50,"y":1220,"wires":[]},{"id":"86110a77.f6e448","type":"server","name":"Home Assistant","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30}]
[…] ist ein Follow-up von den ersten Artikel: Fenix TFT Wifi Thermostat API reverse engineering für Home Assistant mit Node Red Hierbei geht es darum die Temperatur von einem oder mehreren Fenix Thermostaten mit Node-Red […]
[…] wollte die Steuerfunktionen in Node-Red nachbauen, wie ich es auch bei meinen Fenix TFT Wifi Thermostaten gemacht habe. Leider bin ich daran gescheitert, da Segway nach der Authentifizierung zwar JWT-Token […]