A reliable tuya authentication in node-red

In passing when working with tuya and node-red I had a couple of issues, the first one was conflicts with node-red-contrib-crypto-js-dynamic and node-red-contrib-crypto-js. node-red just doesn't seem clever when it comes to duplicate node names. In this example I have replaced the "hmac" function with a copy called mj_hmac. This can be replaced with the hmac from node-red-contrib-crypto-js-dynamic or failing that you will be unable to pass the tuya secret key as a parameter and will have to enter it in the hmac node dialog.

The second issue is that tuya authorisation is on a fixed timeout of two hours.  If you refresh the key at 1 hour and 59 minutes it will only be valid for 1 minute.

This flow keeps track of the time remaining and refreshes just after expiry. I have not seen errors since.

However as a backup plan I do have sub flows that if they see an error reauthenticate. If anyone is reading and interested I can post them as well.


[
{
"id": "741543260888c2aa",
"type": "function",
"z": "629f579f8b89110b",
"name": "",
"func": "var url = \"https://openapi.tuyaeu.com/v1.0/token?grant_type=1\";\nvar t = msg.time;\nvar sign = msg.payload;\nvar client_id = global.get(\"tuya_client_id\");\nmsg.headers ={\n \"sign_method\": \"HMAC-SHA256\",\n \"client_id\" : client_id,\n \"t\": t.toString(),\n \"sign\": sign.toUpperCase(),\n },\n msg.payload = '';\n msg.url = url;\n msg.method = \"GET\";\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 740,
"y": 100,
"wires": [
[
"cb7f1cf923b7fe03"
]
]
},
{
"id": "cb7f1cf923b7fe03",
"type": "http request",
"z": "629f579f8b89110b",
"name": "",
"method": "use",
"ret": "obj",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"senderr": false,
"x": 950,
"y": 100,
"wires": [
[
"4463f90f877f797d"
]
]
},
{
"id": "a08d1cb7247aeb65",
"type": "inject",
"z": "629f579f8b89110b",
"name": "",
"props": [
{
"p": "time",
"v": "",
"vt": "date"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "300",
"crontab": "",
"once": true,
"onceDelay": "0.01",
"topic": "",
"x": 150,
"y": 100,
"wires": [
[
"668d87384a619165"
]
]
},
{
"id": "668d87384a619165",
"type": "function",
"z": "629f579f8b89110b",
"name": "Create signStr",
"func": "var client_id = global.get(\"tuya_client_id\");\nvar t = msg.time;\n\n\nvar method = \"GET\";\nvar sign_url = \"/v1.0/token?grant_type=1\";\n\n// Couldn't get nodered to process an empty string so this is a hash of an empty file\nvar content_hash = \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\";\nvar string_to_sign = method+\"\\n\"+content_hash+\"\\n\"+\"\"+\"\\n\"+sign_url;\nvar signStr = client_id+t+string_to_sign;\n\nmsg.payload = signStr;\nmsg.secrectkey=global.get(\"tuya_secret_key\");\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 320,
"y": 100,
"wires": [
[
"b5bfc9b08123c19d"
]
]
},
{
"id": "4463f90f877f797d",
"type": "function",
"z": "629f579f8b89110b",
"name": "Extract Token from Response",
"func": "if (!msg.payload.hasOwnProperty(\"result\")){\n node.warn(\"failed timed access token reqest\");\n node.warn(msg);\n return;\n}\n\nvar data = msg.payload;\n\nvar access = data.result.access_token;\nvar refresh = data.result.refresh_token;\nvar expire = data.result.expire_time;\nglobal.set(\"tuya_expire\",expire);\nvar creds ={\n \"access_token\" : access,\n \"refresh_token\" : refresh\n}\nglobal.set(\"tuya\", creds);\nmsg.payload = data.result;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1190,
"y": 100,
"wires": [
[]
]
},
{
"id": "b5bfc9b08123c19d",
"type": "mj_hmac",
"z": "629f579f8b89110b",
"name": "",
"algorithm": "HmacSHA256",
"key": "dummy",
"x": 531,
"y": 101,
"wires": [
[
"741543260888c2aa"
]
]
},
{
"id": "4a70a7d93b4729a6",
"type": "comment",
"z": "629f579f8b89110b",
"name": "The token has a two hour life time, seems getting a new token before that time doesn't help. So get a token exactly every 2 hours",
"info": "",
"x": 530,
"y": 220,
"wires": []
},
{
"id": "f843e2db2b7233d7",
"type": "inject",
"z": "629f579f8b89110b",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "time",
"v": "",
"vt": "date"
}
],
"repeat": "1",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "tuya_expire",
"payloadType": "global",
"x": 310,
"y": 160,
"wires": [
[
"6ba4a9afd1dea4ec"
]
]
},
{
"id": "6ba4a9afd1dea4ec",
"type": "function",
"z": "629f579f8b89110b",
"name": "",
"func": "var expire = msg.payload-1;\nglobal.set(\"tuya_expire\",expire);\nif (expire < 0){\n // better late than early\n return [msg,null];\n}else{\n msg.payload = expire;\n return [null,msg];\n}\nreturn msg;",
"outputs": 2,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 520,
"y": 160,
"wires": [
[
"668d87384a619165"
],
[]
]
},
{
"id": "d9d5f18544012106",
"type": "debug",
"z": "629f579f8b89110b",
"name": "",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 750,
"y": 160,
"wires": []
}
]

Comments

Popular posts from this blog

Arduino boards - what I wish I'd known when starting

node-red a full home automation example