# iAqualink for Pool Equipment

**URL:** <https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689>\
**Category:** General\
**Created:** [8 July 2020 02:22 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689 "2020-07-08T02:22:35Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tico](https://avatars.discourse-cdn.com/v4/letter/t/a88e57/32.png) [@Tico](https://discourse.nodered.org/u/Tico)\
**Post date:** [8 July 2020 02:22 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/1 "2020-07-08T02:22:35Z")

</div>

**BACKGROUND**

iAqualink is a service created for connecting Zodiac/Fluidra pool equipment to a cloud-based service for monitoring and control. There are apps for iOS and Android but no open-API for 3rd party control.  
Someone has managed to sniff the login process using a http proxy and published a number of curl commands.

I'd like to create a project to do this with Node-Red but lack sufficient depth of knowledge to get it going. I can see parallels with an existing project for Miele home appliances and I'm trying to adapt this -

[https://flows.nodered.org/flow/32ab59e2399e1b99f786fd40bfa0835a](https://flows.nodered.org/flow/32ab59e2399e1b99f786fd40bfa0835a)

**MANUAL STEPS**

The steps required to login to iAqualink are as follows (note the api\_key is the same for all users) -

```auto
curl -X POST -H "Host:prod.zodiac-io.com" -H "accept:application/json" -H "content-type:application/json" -H "accept-encoding:gzip" -H "user-agent:okhttp/3.12.0" -d '{"api_key":"EOOEMOW4YR6QNB11", "email":"XXXX@XXXX", "password":"XXXXXX"}' "https://prod.zodiac-io.com/users/v1/login"

```

The iAqualink server returns a page of data (X's blank out personal data) -

```auto
{
    "username": "XXXXXXXXXXXXXXXXXX",
    "email": "XXXXXXXXXXX@gmail.com",
    "first_name": "XXX",
    "last_name": "XXXXXX",
    "address": "XXX\n, XXX, XX, au, XXX",
    "address_1": "XXXXXXXXXXX",
    "address_2": null,
    "city": "XXXXXXXXXXX",
    "state": "XX",
    "country": "au",
    "postal_code": "XXXX",
    "id": "XXXXXX",
    "authentication_token": "XXXXXXXXXXXXXXXXXXXX",
    "session_id": "XXXXXXXXXXXXXXXXXXXX",
    "created_at": "2019-12-09T03:12:57.000Z",
    "updated_at": "2019-12-09T03:12:57.000Z",
    "time_zone": "Australia/XXXXXX",
    "phone": "XXXXXXXXXX",
    "opt_in_1": "1",
    "opt_in_2": "0",
    "role": "customer",
    "cognitoPool": {
        "appClientId": "XXXXXXXXXXXXXXXXXXXXX",
        "poolId": "us-east-1_XXXXXXXXX",
        "region": "us-east-1"
    },
    "credentials": {
        "AccessKeyId": "XXXXXXXXXXXXXXXXXXXX",
        "SecretKey": "XXXXXXXXXXXXXXXXXXXXXX",
        "SessionToken": "XXXXXX.............XXXXXXXX",
        "Expiration": "2020-04-26T05:24:59.000Z",
        "IdentityId": "us-east-1:XXXXXXXXXXXXXXXX"
    },
    "userPoolOAuth": {
        "AccessToken": "XXXXXXX...............XXXXXXX",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "XXXXXX..........XXXXXXXXX",
        "IdToken": "XXXXXXXXXX.........XXXXXXXXXXX"
    
}

```

The important fields in the preceeding data block are -

**"id"** : (a 6 digit number that remains the same across logins),

**"authentication\_token"** : (a 20 character field that remains the same across logins),

**"IdToken"** : (a 1740 character field that expires in 3600 seconds).

Armed with this data, a second command will elicit the device serial numbers registered to the user's account (user\_id = id from block above) -

```auto
curl 'https://support.iaqualink.com/devices.json?api_key=EOOEMOW4YR6QNB07&authentication_token=XXXXXXXXXXXXXXXXXXXX&user_id=XXXXXX' -H "Host:support.iaqualink.com" -H "accept:application/json" -H "user-agent:okhttp/3.12.0" -H "content-type:application/json"

```

This is the returned data block for the two devices I have (a chlorinator and a heat pump) -

```auto
[
{
"id":"XXXXXX",
"serial_number":"XXXXXXXXXX",
"created_at":"2020-01-07T09:41:41.000Z",
"updated_at":"2020-01-07T09:41:41.000Z",
"name":"Z400 iQ TD7",
"device_type":"hpm",
"owner_id":null,
"updating":false,
"firmware_version":null,
"target_firmware_version":null,
"update_firmware_start_at":null,
"last_activity_at":null
},
{
"id":"XXXXXX",
"serial_number":"XXXXXXXXXX",
"created_at":"2019-12-09T03:18:18.000Z",
"updated_at":"2019-12-09T03:18:18.000Z",
"name":"eXO-Pro iQ",
"device_type":"exo",
"owner_id":null,
"updating":false,
"firmware_version":null,
"target_firmware_version":null,
"update_firmware_start_at":null,
"last_activity_at":null
}
]

```

**INFORMATION RETRIEVAL AND CONTROL**

With the DEVICE\_SERIAL\_NUMBER ("serial\_number" in the block above), and ID\_TOKEN ("IdToken" from the first block of data) the next **GET** command can be populated to retrieve the pH and ORP values for the chlorinator -

```auto
curl -X GET -H "Host:prod.zodiac-io.com" -H "accept:application/json" -H "authorization:ID_TOKEN" -H "accept-encoding:gzip" -H "user-agent:okhttp/3.12.0" "https://prod.zodiac-io.com/devices/v1/DEVICE_SERIAL_NUMBER/shadow"

```

Control of the chlorinator production (On/Off) can done using a **POST** command ("production" to be set to 0 or 1) -

```auto
curl -X POST -H "Host:prod.zodiac-io.com" -H "accept:application/json" -H "authorization:ID_TOKEN" -H "content-type:application/json; charset=UTF-8" -H "content-length:62" -H "accept-encoding:gzip" -H "user-agent:okhttp/3.12.0" -d '{"state":{"desired":{"equipment":{"swc_0":{"production":0}}}}}' "https://prod.zodiac-io.com/devices/v1/DEVICE_SERIAL_NUMBER/shadow"

```

I have the DEVICE\_SERIAL\_NUMBER for the components in my system (this value is fixed).  
I now need a mechanism to record the variable ID\_TOKEN and subsequently plug this into the commands above.

If anyone has a Node-Red project to suit Zodiac/Fluidra wifi devices, that would be very appreciated.

---

<div class="post-metadata">

**Author:** ![afelix](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/afelix/32/9743_2.png) [@afelix](https://discourse.nodered.org/u/afelix)\
**Post date:** [8 July 2020 05:50 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/2 "2020-07-08T05:50:53Z")

</div>

I’ll likely come back to this when I’ve a laptop at hand, but have you used the builtin http-request node where you manually set headers in a function or change node before through `msg.headers` at the msg object going into the HTTP request node. Looking through your provided samples, it appears pretty easy to me, but let me take a closer look when I’m more awake (and post-breakfast probably) to see if I’m not missing anything.  
If this was my flow to create, I’d likely create subflows for common task, write permanent tokens in the flow directly (hardcoded in a change node or `node-red-contrib-credentials`), then saved in flow context. Then have the Auth tokens that change plus the expiration timestamp saved in an object in flow context and have a subflow that checks if the token has expired yet and if so grabs a new one from the site and stores it back in the context. I’d put that subflow instance right before any node/subflow part that would make use of that token.

---

<div class="post-metadata">

**Author:** ![Tico](https://avatars.discourse-cdn.com/v4/letter/t/a88e57/32.png) [@Tico](https://discourse.nodered.org/u/Tico)\
**Post date:** [8 July 2020 06:38 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/3 "2020-07-08T06:38:30Z")

</div>

Thank-you. I have done a lot of reading and started down the path of setting msg.headers in a Change node before the HTTP request node. That's when it dawned on me that the overall task is far more complex than my meagre skills can grip up.

If it helps, the initial link above to the Miele home appliance project has a structure that seems to match your description. The flow below also has a variation on this structure. I've been trying to adapt this, but with little success.

```auto
[{"id":"36e78bc0.1657a4","type":"subflow","name":"set action","info":"Set (execute) an action for the given appliance. Valid action depend on kind of appliance and state!","category":"Miele","in":[{"x":40,"y":75,"wires":[{"id":"e344f84b.6d63a8"}]}],"out":[{"x":1011,"y":74,"wires":[{"id":"3343768c.58a80a","port":0}]}],"env":[],"color":"#A70625","inputLabels":["appliance id in payload and an action "],"outputLabels":["result"],"icon":"font-awesome/fa-angle-double-right","status":{"x":1010,"y":240,"wires":[{"id":"ba23f155.0ced5","port":0},{"id":"734a9c46.931454","port":0},{"id":"ee31f24e.c3c59","port":0}]}},{"id":"824bdf4d.098d8","type":"http request","z":"36e78bc0.1657a4","name":"PUT to Miele","method":"PUT","ret":"obj","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":499,"y":188,"wires":[["3343768c.58a80a"]]},{"id":"44a71038.b994b","type":"change","z":"36e78bc0.1657a4","name":"url, body","rules":[{"t":"set","p":"url","pt":"msg","to":"\"https://api.mcs3.miele.com/v1/devices/\" & payload & \"/actions/\"","tot":"jsonata"},{"t":"move","p":"action","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":389,"y":68,"wires":[["48edae3a.b6207"]]},{"id":"e344f84b.6d63a8","type":"function","z":"36e78bc0.1657a4","name":"verify input","func":"// verify bearer & msg properties\nif (!global.get('system_bearerMiele')) {\n node.error('global variable system_bearerMiele not defined', msg)\n return [null, {payload:'invalid global variable'}]\n}\nif (!msg.payload){\n node.error('invalid msg.payload - device id', msg)\n return [null, {payload:\"invalid msg.payload - device id\"}]\n}\nif (!msg.action){\n node.error('invalid msg.action - action', msg)\n return [null, {payload:\"invalid msg.action - action\"}]\n}\nreturn [msg, null]","outputs":2,"noerr":0,"x":193,"y":75,"wires":[["44a71038.b994b"],["ee31f24e.c3c59"]]},{"id":"95c24064.ae356","type":"template","z":"36e78bc0.1657a4","name":"header with authorization","field":"headers","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"Authorization\":\"bearer {{global.system_bearerMiele}}\", \"Accept\": \"application/json\", \"Accept-Charset\": \"utf-8\"}","output":"json","x":459,"y":146,"wires":[["824bdf4d.098d8"]]},{"id":"3343768c.58a80a","type":"switch","z":"36e78bc0.1657a4","name":"ok?","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":693,"y":80,"wires":[["ba23f155.0ced5"],["630aed8b.b40564"]]},{"id":"630aed8b.b40564","type":"function","z":"36e78bc0.1657a4","name":"throw error","func":"node.error('http error: ' + msg.payload.message, msg)\nreturn msg\n","outputs":1,"noerr":0,"x":716,"y":160,"wires":[["734a9c46.931454"]]},{"id":"734a9c46.931454","type":"template","z":"36e78bc0.1657a4","name":"error http","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload.message}}\"}","output":"json","x":863,"y":160,"wires":[[]]},{"id":"ba23f155.0ced5","type":"template","z":"36e78bc0.1657a4","name":"ok!","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"green\",\"shape\":\"dot\",\"text\":\"ok!\"}","output":"json","x":873,"y":120,"wires":[[]]},{"id":"ee31f24e.c3c59","type":"template","z":"36e78bc0.1657a4","name":"error input","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload}}\"}","output":"json","x":330,"y":240,"wires":[[]]},{"id":"48edae3a.b6207","type":"json","z":"36e78bc0.1657a4","name":"","property":"payload","action":"obj","pretty":false,"x":399,"y":108,"wires":[["95c24064.ae356"]]},{"id":"7964a41c.2a733c","type":"subflow","name":"get actions","info":"Get an object with all valid actions for the given appliance id at the current appliance state.\n\nPrereq: bearer in global variable system_bearerMiele","category":"Miele","in":[{"x":40,"y":88,"wires":[{"id":"57067fcd.5fa24"}]}],"out":[{"x":1099,"y":38,"wires":[{"id":"cbad3a98.8e5798","port":0}]}],"env":[],"color":"#A70625","inputLabels":["appliance id"],"outputLabels":["all actions"],"icon":"node-red/debug.svg","status":{"x":1099,"y":158,"wires":[{"id":"dd0236a5.619cb8","port":0},{"id":"549c4889.0b7da8","port":0},{"id":"ea2ecac7.77b158","port":0}]}},{"id":"c50ec4c7.e088c8","type":"http request","z":"7964a41c.2a733c","name":"GET from Miele","method":"GET","ret":"obj","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":460,"y":118,"wires":[["cbad3a98.8e5798"]]},{"id":"61db77d9.0edc48","type":"change","z":"7964a41c.2a733c","name":"msg.url, no payload","rules":[{"t":"set","p":"url","pt":"msg","to":"\"https://api.mcs3.miele.com/v1/devices/\" & payload & \"/actions/\"","tot":"jsonata"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":78,"wires":[["c50ec4c7.e088c8"]]},{"id":"d08604e8.d744f8","type":"template","z":"7964a41c.2a733c","name":"msg.headers with authorization","field":"headers","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"Authorization\":\"bearer {{global.system_bearerMiele}}\", \"Accept\": \"application/json\", \"Accept-Charset\": \"utf-8\"}","output":"json","x":429,"y":38,"wires":[["61db77d9.0edc48"]]},{"id":"57067fcd.5fa24","type":"function","z":"7964a41c.2a733c","name":"verify input","func":"// verify bearer & msg properties\nif (!global.get('system_bearerMiele')) {\n node.error('global variable system_bearerMiele not defined', msg)\n return [null, {payload:'invalid global variable'}]\n}\nif (!msg.payload){\n node.error('invalid msg.payload - device id', msg)\n return [null, {payload:\"invalid msg.payload - device id\"}]\n}\nreturn [msg, null]","outputs":2,"noerr":0,"x":198,"y":88,"wires":[["d08604e8.d744f8"],["ea2ecac7.77b158"]]},{"id":"cbad3a98.8e5798","type":"switch","z":"7964a41c.2a733c","name":"ok?","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":703,"y":38,"wires":[["dd0236a5.619cb8"],["db01768f.79f9f8"]]},{"id":"db01768f.79f9f8","type":"function","z":"7964a41c.2a733c","name":"throw error","func":"node.error('http error: ' + msg.payload.message, msg)\nreturn msg\n","outputs":1,"noerr":0,"x":815,"y":118,"wires":[["549c4889.0b7da8"]]},{"id":"549c4889.0b7da8","type":"template","z":"7964a41c.2a733c","name":"error http","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload.message}}\"}","output":"json","x":962,"y":118,"wires":[[]]},{"id":"dd0236a5.619cb8","type":"template","z":"7964a41c.2a733c","name":"ok!","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"green\",\"shape\":\"dot\",\"text\":\"ok!\"}","output":"json","x":972,"y":78,"wires":[[]]},{"id":"ea2ecac7.77b158","type":"template","z":"7964a41c.2a733c","name":"error input","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload}}\"}","output":"json","x":310,"y":158,"wires":[[]]},{"id":"21a52172.bdd0be","type":"subflow","name":"get appliance","info":"Get an object with identification and state data for the given appliance id.\n\nPrereq: bearer in global variable system_bearerMiele","category":"Miele","in":[{"x":40,"y":97,"wires":[{"id":"f5c1f25c.0c873"}]}],"out":[{"x":1143,"y":33,"wires":[{"id":"75456de7.83a4b4","port":0}]}],"env":[{"name":"language","type":"str","value":"en","ui":{"label":{"en-US":"Language"},"type":"select","opts":{"opts":[{"l":{"en-US":"English"},"v":"en"},{"l":{"en-US":"Deutsch"},"v":"de"}]}}}],"color":"#A70625","inputLabels":["appliance id as payload"],"outputLabels":["appliance identification and state"],"icon":"font-awesome/fa-info","status":{"x":1140,"y":164,"wires":[{"id":"eaca9293.ae1e4","port":0},{"id":"a7786538.3aee78","port":0},{"id":"a1dd0fd9.3de","port":0}]}},{"id":"1d15b255.55957e","type":"http request","z":"21a52172.bdd0be","name":"GET from Miele","method":"GET","ret":"obj","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":460,"y":120,"wires":[["75456de7.83a4b4"]]},{"id":"a83812be.5deb5","type":"change","z":"21a52172.bdd0be","name":"msg.url, no payload","rules":[{"t":"set","p":"url","pt":"msg","to":"\"https://api.mcs3.miele.com/v1/devices/\" & payload & \"/?language=\" & $env('language')","tot":"jsonata"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":80,"wires":[["1d15b255.55957e"]]},{"id":"73f8a98e.36af08","type":"template","z":"21a52172.bdd0be","name":"msg.headers with authorization","field":"headers","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"Authorization\":\"bearer {{global.system_bearerMiele}}\", \"Accept\": \"application/json\", \"Accept-Charset\": \"utf-8\"}","output":"json","x":410,"y":40,"wires":[["a83812be.5deb5"]]},{"id":"f5c1f25c.0c873","type":"function","z":"21a52172.bdd0be","name":"verify input","func":"// verify bearer & msg properties\nif (!global.get('system_bearerMiele')) {\n node.error('global variable system_bearerMiele not defined', msg)\n return [null, {payload:'invalid global variable'}]\n}\nif (!msg.payload){\n node.error('invalid msg.payload - device id', msg)\n return [null, {payload:\"invalid msg.payload - device id\"}]\n}\nreturn [msg, null]","outputs":2,"noerr":0,"x":192,"y":97,"wires":[["73f8a98e.36af08"],["a7786538.3aee78"]]},{"id":"75456de7.83a4b4","type":"switch","z":"21a52172.bdd0be","name":"ok?","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":670,"y":40,"wires":[["a1dd0fd9.3de"],["d643daad.b7c6b8"]]},{"id":"d643daad.b7c6b8","type":"function","z":"21a52172.bdd0be","name":"throw error","func":"node.error('http error: ' + msg.payload.message, msg)\nreturn msg\n","outputs":1,"noerr":0,"x":830,"y":120,"wires":[["eaca9293.ae1e4"]]},{"id":"eaca9293.ae1e4","type":"template","z":"21a52172.bdd0be","name":"error http","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload.message}}\"}","output":"json","x":980,"y":120,"wires":[[]]},{"id":"a7786538.3aee78","type":"template","z":"21a52172.bdd0be","name":"error input","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload}}\"}","output":"json","x":350,"y":164,"wires":[[]]},{"id":"a1dd0fd9.3de","type":"template","z":"21a52172.bdd0be","name":"ok!","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"green\",\"shape\":\"dot\",\"text\":\"ok!\"}","output":"json","x":990,"y":80,"wires":[[]]},{"id":"34d56843.0c0358","type":"subflow","name":"get all appliances","info":"Get a list of all appliances with indentification and state.\n\nPrereq: bearer in global variable system_bearerMiele","category":"Miele","in":[{"x":40,"y":80,"wires":[{"id":"4748872d.85c1f8"}]}],"out":[{"x":1119,"y":74,"wires":[{"id":"774ba1f0.40b81","port":0}]}],"env":[{"name":"language","type":"str","value":"en","ui":{"label":{"en-US":"Language"},"type":"select","opts":{"opts":[{"l":{"en-US":"English"},"v":"en"},{"l":{"en-US":"Deutsch"},"v":"de"}]}}}],"color":"#A70625","inputLabels":["just trigger"],"outputLabels":["appliance list in household"],"icon":"font-awesome/fa-list-ul","status":{"x":1120,"y":220,"wires":[{"id":"74dc75d1.9f6abc","port":0},{"id":"2c704f68.3729a","port":0},{"id":"51dcabc.5e9ad54","port":0}]}},{"id":"73e280eb.1bf07","type":"http request","z":"34d56843.0c0358","name":"GET from Miele","method":"GET","ret":"obj","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":480,"y":160,"wires":[["774ba1f0.40b81"]]},{"id":"16f10d8a.86e662","type":"change","z":"34d56843.0c0358","name":"define url delete payload","rules":[{"t":"set","p":"url","pt":"msg","to":"\"https://api.mcs3.miele.com/v1/devices/?language=\" & $env('language')","tot":"jsonata"},{"t":"delete","p":"payload","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":120,"wires":[["73e280eb.1bf07"]]},{"id":"c7df6745.d1d1e8","type":"template","z":"34d56843.0c0358","name":"header with authorization","field":"headers","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"Authorization\":\"bearer {{global.system_bearerMiele}}\", \"Accept\": \"application/json\", \"Accept-Charset\": \"utf-8\"}","output":"json","x":429,"y":80,"wires":[["16f10d8a.86e662"]]},{"id":"4748872d.85c1f8","type":"function","z":"34d56843.0c0358","name":"verify input","func":"// verify token\nif (!global.get('system_bearerMiele')) {\n node.error('global variable system_bearerMiele not defined', msg)\n return [null, {payload:'invalid global variable'}]\n}\nreturn [msg, null]","outputs":2,"noerr":0,"x":218,"y":80,"wires":[["c7df6745.d1d1e8"],["51dcabc.5e9ad54"]]},{"id":"774ba1f0.40b81","type":"switch","z":"34d56843.0c0358","name":"ok?","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":650,"y":80,"wires":[["2c704f68.3729a"],["eb1db9b6.51f118"]]},{"id":"eb1db9b6.51f118","type":"function","z":"34d56843.0c0358","name":"throw error","func":"node.error('http error: ' + msg.payload.message, msg)\nreturn msg\n","outputs":1,"noerr":0,"x":810,"y":160,"wires":[["74dc75d1.9f6abc"]]},{"id":"74dc75d1.9f6abc","type":"template","z":"34d56843.0c0358","name":"error http","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload.message}}\"}","output":"json","x":963,"y":160,"wires":[[]]},{"id":"2c704f68.3729a","type":"template","z":"34d56843.0c0358","name":"ok!","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"green\",\"shape\":\"dot\",\"text\":\"ok!\"}","output":"json","x":954,"y":120,"wires":[[]]},{"id":"51dcabc.5e9ad54","type":"template","z":"34d56843.0c0358","name":"error input","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload}}\"}","output":"json","x":390,"y":218,"wires":[[]]},{"id":"d8085e03.ed6b3","type":"subflow","name":"get bearer","info":"Get a new bearer and refresh token and stores it in global variables. \n\nThe global variables system_bearerMiele and system_refreshMiele are being used.","category":"Miele","in":[{"x":30,"y":63,"wires":[{"id":"6b228a44.84cba4"}]}],"out":[{"x":1221,"y":43,"wires":[{"id":"abcdd5c8.0008b8","port":0}]}],"env":[{"name":"vg","type":"str","value":"en-au","ui":{"label":{"en-US":"Country"},"type":"select","opts":{"opts":[{"l":{"en-US":"Germany"},"v":"de-DE"},{"l":{"en-US":"Australia"},"v":"en-au"}]}}},{"name":"doRefresh","type":"bool","value":"false","ui":{"label":{"en-US":"Use refresh token"},"type":"checkbox","opts":{}}}],"color":"#A70625","inputLabels":["client and user credentials"],"outputLabels":["Miele reponse object"],"icon":"font-awesome/fa-key","status":{"x":1221,"y":254,"wires":[{"id":"eb6b2b96.85e218","port":0},{"id":"8f4ba21f.99fa7","port":0},{"id":"a1be738b.9bd1e","port":0}]}},{"id":"103f2273.95270e","type":"change","z":"d8085e03.ed6b3","name":"get env","rules":[{"t":"set","p":"vg","pt":"msg","to":"vg","tot":"env"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":63,"wires":[["b1fcf9b0.64ebf8"]]},{"id":"2fc77e76.9b70d2","type":"switch","z":"d8085e03.ed6b3","name":"ok?","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"200","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":774,"y":143,"wires":[["eb6b2b96.85e218","abcdd5c8.0008b8"],["af183f22.11e16"]]},{"id":"af183f22.11e16","type":"function","z":"d8085e03.ed6b3","name":"throw error","func":"node.error('http error: ' + msg.payload.message, msg)\nreturn msg\n","outputs":1,"noerr":0,"x":934,"y":223,"wires":[["8f4ba21f.99fa7"]]},{"id":"8f4ba21f.99fa7","type":"template","z":"d8085e03.ed6b3","name":"error http","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload.message}}\"}","output":"json","x":1081,"y":223,"wires":[[]]},{"id":"eb6b2b96.85e218","type":"template","z":"d8085e03.ed6b3","name":"ok!","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"green\",\"shape\":\"dot\",\"text\":\"ok!\"}","output":"json","x":1091,"y":183,"wires":[[]]},{"id":"a1be738b.9bd1e","type":"template","z":"d8085e03.ed6b3","name":"error input","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{\"fill\":\"red\",\"shape\":\"dot\",\"text\":\"error: {{payload}}\"}","output":"json","x":360,"y":258,"wires":[[]]},{"id":"6b228a44.84cba4","type":"function","z":"d8085e03.ed6b3","name":"verify input","func":"// verify bearer & msg properties\nif (!msg.username) {\n node.error('msg.username not defined', msg)\n return [null, {payload:'msg.username not defined'}]\n}\nif (!msg.password){\n node.error('msg.password not defined', msg)\n return [null, {payload:\"msg.password not defined\"}]\n}\nif (!msg.clientId){\n node.error('msg.clientId not defined', msg)\n return [null, {payload:\"msg.clientId not defined\"}]\n}\nif (!msg.clientSecret){\n node.error('msg.clientSecret not defined', msg)\n return [null, {payload:\"msg.clientSecret not defined\"}]\n}\nreturn [msg, null]","outputs":2,"noerr":0,"x":160,"y":63,"wires":[["103f2273.95270e"],["a1be738b.9bd1e"]]},{"id":"305215f6.6cc0ba","type":"template","z":"d8085e03.ed6b3","name":"create body new","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"grant_type=password&username={{username}}&password={{password}}&client_id={{clientId}}&client_secret={{clientSecret}}&vg={{vg}}","output":"str","x":527,"y":103,"wires":[["78040e47.71747"]]},{"id":"78040e47.71747","type":"change","z":"d8085e03.ed6b3","name":"define url, headers","rules":[{"t":"set","p":"url","pt":"msg","to":"https://api.mcs3.miele.com/thirdparty/token/","tot":"str"},{"t":"set","p":"headers","pt":"msg","to":"{\"Content-Type\":\"application/x-www-form-urlencoded\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":754,"y":63,"wires":[["34d76af9.cdd386"]]},{"id":"34d76af9.cdd386","type":"http request","z":"d8085e03.ed6b3","name":"","method":"POST","ret":"obj","paytoqs":false,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":774,"y":103,"wires":[["2fc77e76.9b70d2"]]},{"id":"7ccf7060.bb96","type":"comment","z":"d8085e03.ed6b3","name":"Miele only supports form encoded","info":"","x":530,"y":23,"wires":[]},{"id":"abcdd5c8.0008b8","type":"change","z":"d8085e03.ed6b3","name":"","rules":[{"t":"set","p":"system_bearerMiele","pt":"global","to":"payload.access_token","tot":"msg"},{"t":"set","p":"system_refreshMiele","pt":"global","to":"payload.refresh_token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1001,"y":136,"wires":[[]]},{"id":"39b7be16.653682","type":"template","z":"d8085e03.ed6b3","name":"create body refresh","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"grant_type=refresh_token&refresh_token={{global.system_refreshMiele}}&client_id={{clientId}}&client_secret={{clientSecret}}&vg={{vg}}","output":"str","x":537,"y":63,"wires":[["78040e47.71747"]]},{"id":"b1fcf9b0.64ebf8","type":"switch","z":"d8085e03.ed6b3","name":"","property":"doRefresh","propertyType":"env","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":360,"y":103,"wires":[["39b7be16.653682"],["305215f6.6cc0ba"]]},{"id":"33da6c9c.88ebe4","type":"inject","z":"56863603.2a0738","name":"trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":440,"wires":[["6dbb9704.279428"]]},{"id":"1fb2a00a.b5dc7","type":"comment","z":"56863603.2a0738","name":"B. get information about all devices","info":"","x":266,"y":407,"wires":[]},{"id":"10cc427a.38656e","type":"debug","z":"56863603.2a0738","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":867,"y":690,"wires":[]},{"id":"1e1f4023.a52c9","type":"inject","z":"56863603.2a0738","name":"","topic":"","payload":"000100000636","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":243,"y":613,"wires":[["ee1f9b0c.bb0268"]]},{"id":"bf015d63.e5af4","type":"comment","z":"56863603.2a0738","name":"C. get all information about one specifc device","info":"","x":294,"y":579,"wires":[]},{"id":"6855e393.2f06fc","type":"comment","z":"56863603.2a0738","name":"D. get all actions of one specific device - depend on the current state of the device","info":"","x":404,"y":905,"wires":[]},{"id":"75c3cf45.b8703","type":"comment","z":"56863603.2a0738","name":"A. get new bearer - also do refresh token","info":"","x":284,"y":310,"wires":[]},{"id":"e981efe8.4619e","type":"inject","z":"56863603.2a0738","name":"trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":206,"y":343,"wires":[["a02f485b.72ff88"]]},{"id":"5f0a934.5f9e56c","type":"subflow:d8085e03.ed6b3","z":"56863603.2a0738","name":"","env":[{"name":"vg","value":"de-DE","type":"str"},{"name":"language","value":"de","type":"str"},{"name":"clientId","value":"xx","type":"str"},{"name":"clientSecret","value":"xxx","type":"str"},{"name":"username","value":"xxxxx","type":"str"},{"name":"password","value":"xxxxxx","type":"str"}],"x":510,"y":343,"wires":[["10cc427a.38656e"]]},{"id":"6dbb9704.279428","type":"subflow:34d56843.0c0358","z":"56863603.2a0738","name":"","env":[{"name":"language","value":"en","type":"str"}],"x":373,"y":440,"wires":[["10cc427a.38656e"]]},{"id":"ee1f9b0c.bb0268","type":"subflow:21a52172.bdd0be","z":"56863603.2a0738","name":"","env":[],"x":424,"y":613,"wires":[["10cc427a.38656e"]]},{"id":"624bf1c4.7d2c5","type":"comment","z":"56863603.2a0738","name":"C. get current status of a specifc device","info":"","x":274,"y":733,"wires":[]},{"id":"c333809f.0c254","type":"inject","z":"56863603.2a0738","name":"","topic":"","payload":"000100000636","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":243,"y":766,"wires":[["5ac835f8.05600c"]]},{"id":"5ac835f8.05600c","type":"subflow:21a52172.bdd0be","z":"56863603.2a0738","name":"","env":[],"x":424,"y":766,"wires":[["33c16016.adf44"]]},{"id":"790267c2.7a3a18","type":"comment","z":"56863603.2a0738","name":"C. get current device type of a specifc device","info":"","x":294,"y":657,"wires":[]},{"id":"35be762d.b2ad4a","type":"inject","z":"56863603.2a0738","name":"","topic":"","payload":"000100000636","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":243,"y":690,"wires":[["eda3fd42.51f1f"]]},{"id":"eda3fd42.51f1f","type":"subflow:21a52172.bdd0be","z":"56863603.2a0738","name":"","env":[],"x":424,"y":690,"wires":[["b13ea67e.03e318"]]},{"id":"46a8b13e.24041","type":"comment","z":"56863603.2a0738","name":"B. get device id of first device","info":"","x":244,"y":481,"wires":[]},{"id":"6e23298a.e23d98","type":"inject","z":"56863603.2a0738","name":"trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":209,"y":514,"wires":[["e9983582.3dda98"]]},{"id":"e9983582.3dda98","type":"subflow:34d56843.0c0358","z":"56863603.2a0738","name":"","env":[],"x":370,"y":514,"wires":[["5aa32bba.d64574"]]},{"id":"5aa32bba.d64574","type":"function","z":"56863603.2a0738","name":"id first object","func":"msg.payload = msg.payload[Object.keys(msg.payload)[0]].ident.deviceIdentLabel.fabNumber\nreturn msg;","outputs":1,"noerr":0,"x":552,"y":514,"wires":[["10cc427a.38656e"]]},{"id":"2de9833e.779f8c","type":"comment","z":"56863603.2a0738","name":"C. get remaining time of specific device","info":"","x":274,"y":810,"wires":[]},{"id":"890a99fc.f70b08","type":"inject","z":"56863603.2a0738","name":"","topic":"","payload":"000100000636","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":243,"y":843,"wires":[["7eb7147.08882ec"]]},{"id":"7eb7147.08882ec","type":"subflow:21a52172.bdd0be","z":"56863603.2a0738","name":"","env":[],"x":424,"y":843,"wires":[["880f000e.55508"]]},{"id":"880f000e.55508","type":"change","z":"56863603.2a0738","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.state.remainingTime[0]*60 + payload.state.remainingTime[1] & \" min\"","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":843,"wires":[["10cc427a.38656e"]]},{"id":"b13ea67e.03e318","type":"change","z":"56863603.2a0738","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.ident.type.value_localized","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":690,"wires":[["10cc427a.38656e"]]},{"id":"33c16016.adf44","type":"change","z":"56863603.2a0738","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.state.status.value_localized","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":610,"y":766,"wires":[["10cc427a.38656e"]]},{"id":"7947e7f7.f779e8","type":"subflow:7964a41c.2a733c","z":"56863603.2a0738","name":"","env":[{"name":"language","value":"de","type":"str"}],"x":414,"y":938,"wires":[["10cc427a.38656e"]]},{"id":"86854b98.814cf8","type":"inject","z":"56863603.2a0738","name":"","topic":"","payload":"000100000636","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":243,"y":938,"wires":[["7947e7f7.f779e8"]]},{"id":"83af58f9.576748","type":"comment","z":"56863603.2a0738","name":"E. execute command set name {\"deviceName\":\"My_Devicename\"}","info":"","x":364,"y":998,"wires":[]},{"id":"ffbe2df5.28ba1","type":"inject","z":"56863603.2a0738","name":"trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":213,"y":1030,"wires":[["2317b941.d89696"]]},{"id":"d289f75d.5eeec8","type":"subflow:36e78bc0.1657a4","z":"56863603.2a0738","name":"","env":[{"name":"language","value":"de","type":"str"}],"x":542,"y":1030,"wires":[["10cc427a.38656e"]]},{"id":"932a737c.a8548","type":"subflow:36e78bc0.1657a4","z":"56863603.2a0738","name":"","env":[],"x":542,"y":1111,"wires":[["10cc427a.38656e"]]},{"id":"10405ca4.b7b083","type":"comment","z":"56863603.2a0738","name":"E. execute command stop {\"processAction\":2}","info":"","x":294,"y":1078,"wires":[]},{"id":"2317b941.d89696","type":"change","z":"56863603.2a0738","name":"machine id, action","rules":[{"t":"set","p":"payload","pt":"msg","to":"000149287451","tot":"str"},{"t":"set","p":"action","pt":"msg","to":"{\"deviceName\":\"hkMiele\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":368,"y":1030,"wires":[["d289f75d.5eeec8"]]},{"id":"2b533430.de3dac","type":"inject","z":"56863603.2a0738","name":"trigger","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":213,"y":1111,"wires":[["4429e699.96b598"]]},{"id":"4429e699.96b598","type":"change","z":"56863603.2a0738","name":"machine id, action","rules":[{"t":"set","p":"payload","pt":"msg","to":"000149287451","tot":"str"},{"t":"set","p":"action","pt":"msg","to":"{\"processAction\":2}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":368,"y":1111,"wires":[["932a737c.a8548"]]},{"id":"c98f7f4e.21a51","type":"comment","z":"56863603.2a0738","name":"both commands throw error 204 without error message although action was performed succesfully","info":"","x":1029,"y":996,"wires":[]},{"id":"a02f485b.72ff88","type":"credentials","z":"56863603.2a0738","name":"","props":[{"value":"username","type":"msg"},{"value":"password","type":"msg"},{"value":"clientId","type":"msg"},{"value":"clientSecret","type":"msg"}],"x":349,"y":343,"wires":[["5f0a934.5f9e56c"]]}]

```

---

<div class="post-metadata">

**Author:** ![afelix](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/afelix/32/9743_2.png) [@afelix](https://discourse.nodered.org/u/afelix)\
**Post date:** [8 July 2020 08:33 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/4 "2020-07-08T08:33:32Z")

</div>

I posted one of my flows a bit ago that I still have to publish to the flows library. I’ll see if I can find it on mobile. It uses Spotify’s OAuth API (with the same kind of Bearer and refresh tokens), and uses just about only core nodes to handle everything. My day was just slowed down significantly due to the fire brigade arriving after my upstairs neighbour thought smoking in bed was a good idea. The new infrared smoke sensor stopped him from even lighting his cigarette, so nothing happened. And I’ve now the evidence the alarms/sirens here are top notch too. It will be a while before I take out my laptop to look up flows or write some quick examples though...

---

<div class="post-metadata">

**Author:** ![Tico](https://avatars.discourse-cdn.com/v4/letter/t/a88e57/32.png) [@Tico](https://discourse.nodered.org/u/Tico)\
**Post date:** [17 July 2020 23:51 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/5 "2020-07-17T23:51:14Z")

</div>

Hello @afelix. If this flow is still on your radar, any assistance would be very appreciated.

---

<div class="post-metadata">

**Author:** ![afelix](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/afelix/32/9743_2.png) [@afelix](https://discourse.nodered.org/u/afelix)\
**Post date:** [18 July 2020 08:38 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/6 "2020-07-18T08:38:35Z")

</div>

Thanks for the reminder. After another week from hell I truly forgot. Dug it up in a forum search, here you go.

> [@Valid bearer token](https://discourse.nodered.org/t/valid-bearer-token/28633/2):
>
> I have a flow that consumes the Spotify API that does this, let me sanitise it a bit and upload it for you. This token is valid for 1 hour. There's 2 options you can take: request either way and if it comes back as invalid refresh and store the token again, or keep a timestamp of the last refresh and if it's longer ago than 95% of your token expiration time refresh it just to be safe that it doesn't run out during your request sequence. I have the OAuth callback set up in an http-in/http-respon…

---

<div class="post-metadata">

**Author:** ![Tico](https://avatars.discourse-cdn.com/v4/letter/t/a88e57/32.png) [@Tico](https://discourse.nodered.org/u/Tico)\
**Post date:** [18 July 2020 12:27 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/7 "2020-07-18T12:27:38Z")

</div>

Thanks. I'll try it out.

---

<div class="post-metadata">

**Author:** ![Tico](https://avatars.discourse-cdn.com/v4/letter/t/a88e57/32.png) [@Tico](https://discourse.nodered.org/u/Tico)\
**Post date:** [9 August 2020 03:12 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/8 "2020-08-09T03:12:29Z")

</div>

Hello @afelix. I've spent a fair bit of time trying to adapt the Spotify API without success. The problem is that I know next to nothing about coding or Node-Red. I've tried the 'technique' of 100+ educated guesses to modify this for my application. If I try 1000 different guesses I might get there...but probably not. 😕

If you have any time to look at the iAqualink process descibed above, any specific guidance (set at numpty level) would be very welcome.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [8 October 2020 03:12 UTC](https://discourse.nodered.org/t/iaqualink-for-pool-equipment/29689/9 "2020-10-08T03:12:31Z")

</div>

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.
