Skip to content

Instantly share code, notes, and snippets.

@MorningZ
Last active April 25, 2020 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MorningZ/637e4c7bd6864e85aba6218f6dd772fa to your computer and use it in GitHub Desktop.
Save MorningZ/637e4c7bd6864e85aba6218f6dd772fa to your computer and use it in GitHub Desktop.
MyQ Flow for Node-RED / MQTT
[{"id":"507826e5.3ffbf8","type":"inject","z":"874c5955.ab41f8","name":"Status: Timer","topic":"garage/status","payload":"","payloadType":"date","repeat":"30","crontab":"","once":true,"onceDelay":"","x":220,"y":720,"wires":[["b1c4af88.5978d"]]},{"id":"23b3616.20e559e","type":"http request","z":"874c5955.ab41f8","name":"Call for token","method":"POST","ret":"obj","paytoqs":false,"url":"https://myqexternal.myqdevice.com/api/v4/User/Validate","tls":"","persist":false,"proxy":"","authType":"","x":875,"y":680,"wires":[["49214e77.d3055"]],"l":false},{"id":"e34ac162.30a6a","type":"debug","z":"874c5955.ab41f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1015,"y":680,"wires":[],"l":false},{"id":"968bcaf0.5e0e68","type":"function","z":"874c5955.ab41f8","name":"Credentials","func":"\nreturn {\n \"payload\": {\n \"username\" : \"XXXXXXXXX\",\n \"password\" : \"XXXXXXXX\"\n },\n \"headers\": {\n ContentType : \"application/json\",\n Accept : \"application/json\",\n MyQApplicationId : \"Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i\",\n Culture : \"en\"\n }\n};","outputs":1,"noerr":0,"x":750,"y":680,"wires":[["23b3616.20e559e"]]},{"id":"b1c4af88.5978d","type":"function","z":"874c5955.ab41f8","name":"","func":"\nif(msg.payload && msg.payload.desired_state) {\n msg.desired_state = msg.payload.desired_state;\n}\n\n\nmsg.payload = {};\n\nvar myq = global.get(\"myq\");\n\nif(myq && myq.SecurityToken) {\n msg.payload.myq = myq; \n}\n\n\nreturn msg;\n","outputs":"1","noerr":0,"x":435,"y":680,"wires":[["1c87a52e.7e439b","6202206f.e7c1a","47d33c73.653994"]],"l":false},{"id":"c36bcbc8.860e78","type":"debug","z":"874c5955.ab41f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":695,"y":640,"wires":[],"l":false},{"id":"49214e77.d3055","type":"function","z":"874c5955.ab41f8","name":"","func":"\nif(msg.statusCode == 200 && msg.payload.SecurityToken) {\n var myq = {\n SecurityToken : msg.payload.SecurityToken\n };\n \n global.set(\"myq\", myq);\n msg.payload.myq = myq;\n return msg;\n} else {\n return null;\n}\n\n\n","outputs":1,"noerr":0,"x":935,"y":680,"wires":[["e34ac162.30a6a","47d33c73.653994"]],"l":false},{"id":"1c87a52e.7e439b","type":"switch","z":"874c5955.ab41f8","name":"","property":"payload.myq","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":575,"y":680,"wires":[["c36bcbc8.860e78"],["968bcaf0.5e0e68","c36bcbc8.860e78"]],"l":false},{"id":"6202206f.e7c1a","type":"debug","z":"874c5955.ab41f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":515,"y":640,"wires":[],"l":false},{"id":"47d33c73.653994","type":"function","z":"874c5955.ab41f8","name":"","func":"\nvar myq;\n\nif(msg.payload.myq) {\n myq = msg.payload.myq;\n} else {\n myq = global.get(\"myq\");\n}\n\nif(!myq) return msg;\n\n\n\n\nmsg.headers = {\n ContentType : \"application/json\",\n Accept : \"application/json\",\n MyQApplicationId : \"Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i\",\n Culture : \"en\",\n SecurityToken : myq.SecurityToken\n}\n\n\nreturn msg;\n\n","outputs":1,"noerr":0,"x":495,"y":760,"wires":[["da42ce7f.83aff"]],"l":false},{"id":"c788f7c4.ec6948","type":"http request","z":"874c5955.ab41f8","name":"","method":"GET","ret":"obj","paytoqs":false,"url":"https://myqexternal.myqdevice.com/api/v4/UserDeviceDetails/Get","tls":"","persist":false,"proxy":"","authType":"","x":715,"y":760,"wires":[["db2027af.300d08","3486ae37.022192"]],"l":false},{"id":"cb2d4097.22693","type":"function","z":"874c5955.ab41f8","name":"","func":"\nvar myq = global.get(\"myq\");\n\nmyq.devices = msg.payload.Devices;\nmyq.timestamp = new Date();\n\nglobal.set(\"myq\", myq);\n\nmsg.payload.myq = myq;\n\nvar state = \"unknown\";\nfor (var d in myq.devices) {\n var device = myq.devices[d];\n if(device && device.MyQDeviceTypeName && device.MyQDeviceTypeName == \"GarageDoorOpener\" && device.Attributes) {\n myq.garage = device;\n for(var a in device.Attributes) {\n var attribute = device.Attributes[a];\n if(attribute && attribute.AttributeDisplayName == \"doorstate\") {\n state = attribute.Value + \"\";\n myq.garage.state = \"unknown\";\n if(state == \"1\") myq.garage.state = \"open\";\n if(state == \"2\") myq.garage.state = \"closed\";\n if(state == \"3\") myq.garage.state = \"stopped\";\n if(state == \"4\") myq.garage.state = \"opening\";\n if(state == \"5\") myq.garage.state = \"closing\";\n if(state == \"8\") myq.garage.state = \"moving\";\n if(state == \"9\") myq.garage.state = \"open\";\n }\n }\n }\n}\nif(msg.headers) delete msg.headers;\nif(msg.statusCode) delete msg.statusCode;\nif(msg.responseUrl) delete msg.responseUrl;\nif(msg.responseCookies) delete msg.responseCookies;\n\nmsg.payload = msg.payload.myq;\n\nvar last = global.get(\"myq_garage_last_known\")\nmsg.payload.garage.hasChanged = false;\nif(last && last != msg.payload.garage.state) {\n msg.payload.garage.hasChanged = true;\n}\nglobal.set(\"myq_garage_last_known\", msg.payload.garage.state);\n\nreturn msg;","outputs":1,"noerr":0,"x":855,"y":860,"wires":[["782e6a8f.389834","8b4e29c4.a2d5a8","553e7b4d.6744a4"]],"l":false},{"id":"db2027af.300d08","type":"debug","z":"874c5955.ab41f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":915,"y":760,"wires":[],"l":false},{"id":"ddeb43c.99d7ac","type":"switch","z":"874c5955.ab41f8","name":"Path based on error","property":"payload.error","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":775,"y":820,"wires":[["224359ea.58fea6"],["cb2d4097.22693"]],"l":false},{"id":"224359ea.58fea6","type":"function","z":"874c5955.ab41f8","name":"","func":"\nreturn { _msgid : msg._msgid }\n","outputs":1,"noerr":0,"x":855,"y":820,"wires":[["968bcaf0.5e0e68"]],"l":false},{"id":"3486ae37.022192","type":"function","z":"874c5955.ab41f8","name":"Capture error","func":"\n\nif (msg.payload.ErrorMessage && msg.payload.ErrorMessage !== \"\") {\n msg.payload.error = msg.payload.ErrorMessage;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":715,"y":820,"wires":[["ddeb43c.99d7ac"]],"l":false},{"id":"2e017072.5b181","type":"comment","z":"874c5955.ab41f8","name":"MyQ MQTT Interface","info":"https://github.com/jbnunn/Alexa-MyQGarage/blob/master/myq.py\n\nhttps://unofficialliftmastermyq.docs.apiary.io/","x":240,"y":620,"wires":[]},{"id":"11776618.cff2ca","type":"inject","z":"874c5955.ab41f8","name":"Status: Inject","topic":"garage/status","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":"","x":210,"y":680,"wires":[["b1c4af88.5978d"]]},{"id":"782e6a8f.389834","type":"mqtt out","z":"874c5955.ab41f8","name":"","topic":"garage","qos":"","retain":"","broker":"f0bbcb39.fee3a8","x":1010,"y":820,"wires":[]},{"id":"8b4e29c4.a2d5a8","type":"switch","z":"874c5955.ab41f8","name":"","property":"payload.garage.hasChanged","propertyType":"msg","rules":[{"t":"true"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":915,"y":920,"wires":[["3da5cbc8.6e46f4","553e7b4d.6744a4"],["553e7b4d.6744a4"]],"l":false},{"id":"553e7b4d.6744a4","type":"debug","z":"874c5955.ab41f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":975,"y":880,"wires":[],"l":false},{"id":"3da5cbc8.6e46f4","type":"function","z":"874c5955.ab41f8","name":"Message","func":"\nlet txt = msg.payload.garage.state;\n\nswitch (txt) {\n case \"open\": case \"opening\": txt = \"Opening\"; break; \n case \"opened\": txt = \"Opened\"; break; \n case \"close\": case \"closing\": txt = \"Closing\"; break; \n case \"closed\": txt = \"Closed\"; break; \n}\n\nlet pad = function(num) {\n return (num < 10) ? \"0\" + num : \"\" + num;\n}\n\nlet dt = new Date(msg.payload.timestamp); let hrs = dt.getHours();\nif (hrs === 0) { // Midnight hour\n dt = \"12:\" + dt.getMinutes() + \" am\";\n}\nelse if (hrs > 11) { // PM\n hrs = hrs - 12;\n dt = (hrs < 10 ? \"0\" + hrs : \"\" + hrs) + \":\" + dt.getMinutes() + \" pm\";\n}\nelse { // AM \n dt = (hrs < 10 ? \"0\" + hrs : \"\" + hrs) + \":\" + dt.getMinutes() + \" am\";\n}\n\nreturn {\n \"payload\": {\n \"type\": 'pushNotification', \n \"payload\": { \n title: 'Garage Door', \n text: txt + \" at \" + dt \n }\n }\n};\n\n","outputs":1,"noerr":0,"x":1020,"y":920,"wires":[["a5f7c8c4.84d668"]]},{"id":"7cf27829.570748","type":"inject","z":"874c5955.ab41f8","name":"Control: Close","topic":"garage/control","payload":"{\"desired_state\":\"close\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":"","x":210,"y":880,"wires":[["b1c4af88.5978d"]]},{"id":"b63c7c5d.87899","type":"http request","z":"874c5955.ab41f8","name":"","method":"PUT","ret":"obj","paytoqs":false,"url":"https://myqexternal.myqdevice.com/api/v4/DeviceAttribute/PutDeviceAttribute","tls":"","persist":false,"proxy":"","authType":"","x":615,"y":820,"wires":[["91f0102.12453f","d19d1d10.eff6d"]],"l":false},{"id":"53d039a6.2ae7a8","type":"function","z":"874c5955.ab41f8","name":"","func":"var myq;\n\nif (msg.payload.myq) {\n myq = msg.payload.myq;\n} else {\n myq = global.get(\"myq\");\n}\nvar open_close_state = 0; \nif (msg.desired_state && msg.desired_state == \"open\") open_close_state = 1;\n\nmsg.payload = {\n \"attributeName\": \"desireddoorstate\",\n \"myQDeviceId\": myq.garage.MyQDeviceId,\n \"AttributeValue\": open_close_state\n};\n\nreturn msg;","outputs":1,"noerr":0,"x":515,"y":820,"wires":[["b63c7c5d.87899","91f0102.12453f"]],"l":false},{"id":"da42ce7f.83aff","type":"switch","z":"874c5955.ab41f8","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"garage/control","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":555,"y":760,"wires":[["53d039a6.2ae7a8"],["c788f7c4.ec6948"]],"l":false},{"id":"91f0102.12453f","type":"debug","z":"874c5955.ab41f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":715,"y":940,"wires":[],"l":false},{"id":"50e2f908.2ddca8","type":"inject","z":"874c5955.ab41f8","name":"Control: Open","topic":"garage/control","payload":"{\"desired_state\":\"open\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":"","x":210,"y":840,"wires":[["b1c4af88.5978d"]]},{"id":"d19d1d10.eff6d","type":"mqtt out","z":"874c5955.ab41f8","name":"State","topic":"garage/control/state","qos":"","retain":"false","broker":"f0bbcb39.fee3a8","x":750,"y":880,"wires":[]},{"id":"8bdf3615.b545d8","type":"mqtt in","z":"874c5955.ab41f8","name":"Control: MQTT","topic":"garage/control","qos":"2","datatype":"json","broker":"f0bbcb39.fee3a8","x":220,"y":920,"wires":[["b1c4af88.5978d","7016175e.dfcde8"]]},{"id":"4c3dfa2d.b3ba54","type":"mqtt in","z":"874c5955.ab41f8","name":"Status: MQTT In","topic":"garage/status","qos":"2","datatype":"auto","broker":"f0bbcb39.fee3a8","x":220,"y":760,"wires":[["b1c4af88.5978d"]]},{"id":"a5f7c8c4.84d668","type":"alexa-remote-routine","z":"874c5955.ab41f8","name":"Send push notification","account":"41ad82a9.09a59c","routineNode":{"type":"custom","payload":{"type":"msg","value":"payload"}},"x":1115,"y":920,"wires":[[]],"icon":"font-awesome/fa-mobile-phone","l":false},{"id":"7016175e.dfcde8","type":"debug","z":"874c5955.ab41f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":375,"y":920,"wires":[],"l":false},{"id":"dd37364a.6c1238","type":"inject","z":"874c5955.ab41f8","name":"Example \"open\" call from outside this flow","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":880,"y":540,"wires":[["64451aa.233cde4"]]},{"id":"64451aa.233cde4","type":"function","z":"874c5955.ab41f8","name":"Example of MQTT message creation","func":"\n// Make sure that \"Retain\" is set to \"true\" on the \"MQTT Out\" node that this flows into! \n\nreturn {\n \"topic\": \"garage/control\", \n \"payload\": { \"desired_state\": msg.payload ? \"open\" : \"close\" } \n};\n","outputs":1,"noerr":0,"x":1081,"y":560,"wires":[["9cc98833.3a2978"]],"inputLabels":["Open"],"outputLabels":["Close"],"icon":"node-red-contrib-sun-position/switch-black.png","l":false},{"id":"9cc98833.3a2978","type":"mqtt out","z":"874c5955.ab41f8","name":"","topic":"","qos":"2","retain":"true","broker":"cba97092.99da5","x":1170,"y":560,"wires":[]},{"id":"e68277b9.8ba748","type":"inject","z":"874c5955.ab41f8","name":"Example \"close\" call from outside this flow","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":880,"y":580,"wires":[["64451aa.233cde4"]]},{"id":"f0bbcb39.fee3a8","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"41ad82a9.09a59c","type":"alexa-remote-account","z":"","name":"Alexa Account","authMethod":"proxy","proxyOwnIp":"10.21.0.71","proxyPort":"3456","cookieFile":"authFile","refreshInterval":"3","alexaServiceHost":"pitangui.amazon.com","amazonPage":"amazon.com","acceptLanguage":"en-US","userAgent":"","useWsMqtt":"on","autoInit":"off"},{"id":"cba97092.99da5","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]
@MorningZ
Copy link
Author

MorningZ commented Apr 25, 2020

Reference:
https://flows.nodered.org/flow/83f263d6f5f23e53e2aed85b96aadb3e
https://unofficialliftmastermyq.docs.apiary.io/

I just cleaned things up a little bit.

Installed on a Raspberry Pi running Node-RED and Mosquito MQTT and this works fantastic

So far the only issue i've seen is that it can send multiple push notifications at the end once for in-process and another when completed (like one notification for "opening" and another for "open"). That could easily be fixed with a flow variable to tame that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment