Choose style:

Author Topic: connecting node-red to the Almond+ websocket  (Read 4909 times)

0 Members and 1 Guest are viewing this topic.

Offline czyzczyz

  • Backer
  • *
  • Posts: 49
  • Thanks: 0
  • Registered : 23/08/2014
    YearsYearsYearsYearsYearsYearsYears
connecting node-red to the Almond+ websocket
« on: July 19, 2017, 02:38:39 pm »
I'm finally playing with the official websocket API and node-red, with the goal of completely integrating the almond+'s automation features into node-red. I'm coming at this from the world's most basic and incomplete understanding of node.js, websockets, and every related topic. So it's going to be a while. But step by step hopefully I'll get there. The eventual goal would be to have a DeviceList command in node-red build up a list of devices to which one could send commands, and that also listens for changes to all connected devices uses them to trigger other events.

First step, getting a node-red flow to talk to the Almond+. This has been a little difficult. The documentation for the Almond+'s websocket API says the connection should be in the form:

Quote
<webinterfaceUrl>:<portid>/<Login>/<password>
Example ws://10.10.10.254:7681/root/frank

But as it happens, this never seems to actually connect when I use this format for a websocket in node-red, and when I connect to such a websocket URL using the 'Simple Web Socket Client' extension in Chrome, it connects there but I can't get any commands to work. In Node-red, as soon as I deploy a flow that includes that URL, I get a debug message of "[msg] : error
ReferenceError: tout is not defined". I figure maybe "tout" means there's no target for commands at the end of the URL, and when I played with this back before the API was released there was such an endpoint in the URL, "twirp".

A bit of experimentation with more-ordinary URL authentication formatting and that endpoint reference got me to an apparently functional websocket of this form:

Quote
ws://<Login>:<password>@10.0.1.1:<portid>/twirp
Example ws://root:mypassword@10.0.1.1:7681/twirp

I don't know if this is completely nonstandard and pre-API and will break down under usage, but in Node-red I'm now seeing messages coming to me from the Almond+'s websocket, and even got the DeviceList command to work. It's a starting point. For what it's worth, I did try using a 'wss://' URL, but I guess secure websockets are not supported.

Here's a little demo video of the flow in action: https://vimeo.com/226193352

If anyone wants to try out this flow, here's a password-redacted copy that can be paste-imported into node-red:

[{"id":"e28f2e6b.40397","type":"websocket-client","path":"ws://root:my_password@10.0.1.1:7681/twirp","wholemsg":"false"},{"id":"18198b69.ec86c5","type":"debug","name":"from receive from almond+","active":true,"console":"false","complete":"payload","x":655,"y":176,"z":"99b24811.70a2d8","wires":[]},{"id":"3c8520ef.9f937","type":"websocket out","name":"websocket out","server":"","client":"e28f2e6b.40397","x":714,"y":341,"z":"99b24811.70a2d8","wires":[]},{"id":"5e6a1e75.0a02","type":"inject","name":"Get list of devices","topic":"","payload":"DeviceList","payloadType":"string","repeat":"","crontab":"","once":false,"x":195,"y":438,"z":"99b24811.70a2d8","wires":[["9c12367b.a03f18"]]},{"id":"a834d6d7.fbc678","type":"websocket in","name":"connect from almond+ new","server":"","client":"e28f2e6b.40397","x":228,"y":146,"z":"99b24811.70a2d8","wires":[["18198b69.ec86c5","d54cf268.9f9b5"]]},{"id":"7d5ee261.0b2d1c","type":"debug","name":"message sent","active":true,"console":"false","complete":"payload","x":703,"y":452,"z":"99b24811.70a2d8","wires":[]},{"id":"9c12367b.a03f18","type":"function","name":"add instance id","func":"var newmsg=[];\nrnd = Math.round (Math.random() * 10000) ;\n\nnewmsg.push({\"MobileInternalIndex\" : rnd, \"CommandType\" : msg.payload});\n// return '{payload: \"mii\":\"21234\",\"cmd\":\"devicelist\"}'\n\nmsg.payload = newmsg[0];\nreturn msg;","outputs":1,"noerr":0,"x":385,"y":375,"z":"99b24811.70a2d8","wires":[["3c8520ef.9f937","7d5ee261.0b2d1c"]]},{"id":"d54cf268.9f9b5","type":"file","name":"write almond log","filename":"almond_log.txt","appendNewline":true,"overwriteFile":"false","x":738,"y":610,"z":"99b24811.70a2d8","wires":[]}]



Offline Shazster

  • Newbie
  • ****
  • Posts: 173
  • Thanks: 0
  • Registered : 29/11/2015
    YearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #1 on: July 21, 2017, 11:11:02 pm »
Well that is progress.
Not so sure how cool I am with unsecured websocket access, but it's definitely a new avenue to tinker.

Offline czyzczyz

  • Backer
  • *
  • Posts: 49
  • Thanks: 0
  • Registered : 23/08/2014
    YearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #2 on: October 17, 2017, 07:25:21 pm »
I've finally got a functional node-red setup at home, and have in the process learned more about how it operates, so will be revisiting this.

Offline vansens

  • Backer
  • *
  • Posts: 29
  • Thanks: 0
  • Registered : 02/08/2013
    YearsYearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #3 on: November 21, 2017, 01:55:34 am »
Please do! This looks very promising!

Offline czyzczyz

  • Backer
  • *
  • Posts: 49
  • Thanks: 0
  • Registered : 23/08/2014
    YearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #4 on: November 21, 2017, 02:19:07 pm »
Here's my current work-in-progress. Copy it to the clipboard, import the clipboard into node-red, change the almond IP and password, and you'll have a set of nodes that monitor for messages from the Almond and can pass them along to other node-red functions. I'm currently using it to pass along information from an energy monitor to the emoncms service, and to monitor for my garage's tilt sensor and update a garage device in homebridge.

I've attached an image which shows a big function node with multiple outputs called "split based on device id in topic". In there, I've written a function that splits the incoming message from the almond and sends it out the output whose number matches the number by which the Almond has enumerated that particular device/sensor. After that node, one can do some fairly normal node-red stuff.



[{"id":"d6f09602.7975b8","type":"function","z":"bed89ec1.30a6","name":"split based on device id in topic","func":"device_number = parseInt(\n    msg.topic.substring(\n        msg.topic.lastIndexOf(\"/\") + 1,\n        msg.topic.length\n        ),\n        10\n    );\n\nnode.error(\"device_number is \" + device_number);\n\nmsg_array = new Array(13).fill(null); //set up an array of outputs\n\nmsg_array[device_number] = msg; //incoming msg is routed only to the device's output\n\nnode.error(msg_array);\n\nreturn msg_array;","outputs":"13","noerr":0,"x":1030,"y":1000,"wires":[[],[],[],[],[],["3254dafc.6d5336"],[],[],[],[],[],["3bd3cc7f.ddff34"],[]],"outputLabels":["","","","","","","","","","","11","",""]},{"id":"5f661adc.91b6e4","type":"function","z":"bed89ec1.30a6","name":"change topic to almond device","func":"//input looks like\n//payload.Devices[\"11\"].DeviceValues[\"2\"]\n\nvar device_number = Object.keys(msg.payload.Devices)[0];\n//node.error (\"Device is number \" + device_number );\n\n//change topic to include device number\nmsg.topic = \"almond_device_update/\"+device_number;\n\nreturn msg;\n \n","outputs":1,"noerr":0,"x":730,"y":1060,"wires":[["d6f09602.7975b8"]]},{"id":"47f41454.0f395c","type":"debug","z":"bed89ec1.30a6","name":"test split output debug","active":true,"console":"false","complete":"payload","x":700,"y":960,"wires":[]},{"id":"9c11571c.8a98c8","type":"switch","z":"bed89ec1.30a6","name":"switch - case: DynamicIndexUpdated","property":"payload.CommandType","propertyType":"msg","rules":[{"t":"eq","v":"DynamicIndexUpdated","vt":"str"},{"t":"eq","v":"DynamicClientUpdated","vt":"str"}],"checkall":"true","outputs":2,"x":590,"y":1000,"wires":[["47f41454.0f395c","5f661adc.91b6e4"],[]],"outputLabels":["DynamicIndexUpdated","DynamicClientUpdated"]},{"id":"d0180633.8b5cf8","type":"json","z":"bed89ec1.30a6","name":"","pretty":true,"x":370,"y":1000,"wires":[["9c11571c.8a98c8"]]},{"id":"d2cb86ee.568958","type":"debug","z":"bed89ec1.30a6","name":"from receive from almond+","active":false,"console":"false","complete":"payload","x":300,"y":920,"wires":[]},{"id":"17de8a42.23a716","type":"file","z":"bed89ec1.30a6","name":"write almond log","filename":"almond_log.txt","appendNewline":true,"overwriteFile":"false","x":250,"y":1060,"wires":[]},{"id":"b8640268.63c85","type":"websocket in","z":"bed89ec1.30a6","name":"listen to almond+ messages","server":"","client":"6a76d828.cff8f8","x":140,"y":980,"wires":[["17de8a42.23a716","d2cb86ee.568958","d0180633.8b5cf8"]]},{"id":"3254dafc.6d5336","type":"function","z":"bed89ec1.30a6","name":"simple name:value pair","func":"//payload.Devices[\"11\"].DeviceValues[\"2\"]\n\nvar device_number = Object.keys(msg.payload.Devices)[0];\n//node.error (\"Device is number \" + device_number );\n\nvar device_values_object_number = Object.keys(msg.payload.Devices[device_number].DeviceValues)[0];\n//node.error (\"Device values_object_number is \" + device_values_object_number );\n\n//node.error (msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Name + \" : \" + msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Value);\n\n\n\nmsg.payload = msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Name + \": \" + msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Value;\n\nnode.error (\"last bit of topic: \" + msg.topic.substring(msg.topic.lastIndexOf(\"/\") + 1, msg.topic.length));\n\nreturn msg;\n \n","outputs":1,"noerr":0,"x":1310,"y":1020,"wires":[["51a56b3e.c9c334","418da39b.9c130c"]]},{"id":"3bd3cc7f.ddff34","type":"function","z":"bed89ec1.30a6","name":"simple name:value pair","func":"//payload.Devices[\"11\"].DeviceValues[\"2\"]\n\nvar device_number = Object.keys(msg.payload.Devices)[0];\n//node.error (\"Device is number \" + device_number );\n\nvar device_values_object_number = Object.keys(msg.payload.Devices[device_number].DeviceValues)[0];\n//node.error (\"Device values_object_number is \" + device_values_object_number );\n\n//node.error (msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Name + \" : \" + msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Value);\n\n\n\nmsg.payload = msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Name + \": \" + msg.payload.Devices[device_number].DeviceValues[device_values_object_number].Value;\n\nnode.error (\"last bit of topic: \" + msg.topic.substring(msg.topic.lastIndexOf(\"/\") + 1, msg.topic.length));\n\nreturn msg;\n \n","outputs":1,"noerr":0,"x":1310,"y":1060,"wires":[["24e80a2.87025f6","51a56b3e.c9c334"]]},{"id":"51a56b3e.c9c334","type":"debug","z":"bed89ec1.30a6","name":"after split function","active":true,"console":"false","complete":"payload","x":1590,"y":1020,"wires":[]},{"id":"6a76d828.cff8f8","type":"websocket-client","path":"ws://root:myAlmondPassword@MyLocalAlmondIPAddress:7681/twirp","wholemsg":"false"}]


« Last Edit: November 21, 2017, 02:28:04 pm by czyzczyz »

Offline vansens

  • Backer
  • *
  • Posts: 29
  • Thanks: 0
  • Registered : 02/08/2013
    YearsYearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #5 on: November 22, 2017, 11:36:13 am »
Thank you for posting this. I have node-red running locally on the same LAN as the Almond+, but I cannot seem to get anything out of the websocket. Debug is empty and I just see "disconnected" flash up below the input "listen to almond+ messages"
Is there a different way to test if the API is actually responding? Which firmware are you running?

Offline czyzczyz

  • Backer
  • *
  • Posts: 49
  • Thanks: 0
  • Registered : 23/08/2014
    YearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #6 on: November 22, 2017, 11:54:45 am »
Hmm, you may have to make a new web socket listener and wire it in in place of mine.

Firmware-wise Iím running the latest. I forget the number. Iím away from my local network at the moment so am not sure. r090?

Offline vansens

  • Backer
  • *
  • Posts: 29
  • Thanks: 0
  • Registered : 02/08/2013
    YearsYearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #7 on: November 22, 2017, 08:10:10 pm »
I run the latest as well. I tried creating a new node but the same thing happens. How can I verify that the Almond+ is actually responding correctly? How did you find out about the "/twirp" part? I just want to see if I get any output, even without using node-red.

Edit: I got it working, however the endpoint I needed to use is

ws://192.168.1.72:7681/root_password


I can finally see all messages come through now and move on. Cheers!
« Last Edit: November 23, 2017, 09:15:58 pm by vansens »

Offline czyzczyz

  • Backer
  • *
  • Posts: 49
  • Thanks: 0
  • Registered : 23/08/2014
    YearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #8 on: December 14, 2017, 05:14:50 pm »
Oh wow. I don't know why I had to set things up with the 'twirp' thing, I think I snarfed that from the source of the javascript that runs my almond plus's admin web pages, or maybe I spotted it when I was trying to snoop on websocket communcations to figure out how to interface with the thing before there was an API. In any case, glad you've got it working for you. With any luck my nodes will help in some way.

Offline czyzczyz

  • Backer
  • *
  • Posts: 49
  • Thanks: 0
  • Registered : 23/08/2014
    YearsYearsYearsYearsYearsYearsYears
Re: connecting node-red to the Almond+ websocket
« Reply #9 on: January 07, 2018, 06:06:56 pm »
I've successfully got my nodes listening for notifications from the Almond Plus, but I haven't yet tried sending a command to a z-wave device connected to it. Have you tried? I'm curious if there are any tricks to it.

 

Page created in 0.039 seconds with 18 queries.

bottleneck