Leverage the trend API (for ANY groov Device type) for some Edge computing


Hey groov fans…

Wanna grab some data from your groov in a batch? From any Device type? Here’s an example in Node-RED, but you could do something similar in your language of choice (anything that can grab a file via a URL and parse it).

I took this fancier example from @torchard to show how you can do a little Edge computing, or at perhaps Edgy?? :wink:

In this example, we’re just doing a sum over one minute’s worth of data points. You could do an average or look for anomalies if you wanted.

Node-RED note: this is also a back-door way to get your groov Device tags (e.g. Modbus, OPC-UA, System, Simulation, or whatever you couldn’t normally access) via Node-RED, and least for reading/monitoring.

Since a trend of ANY device type has a API/endpoint/url for each pen, you can grab that data and have your way with it. (In this case, summing the last minute worth of data: 12 x 5 second samples.)

Don’t forget to sanity check your floating-point values, especially if you’re doing a sum as in this example, so as not to clobber your running total w/a NAN.

Note in the debug window to the right, you see my total, which is a pulse (on/off every 5 seconds for my total of 6 on=1 plus 6 off=0 for the 12 samples per minute).

Happy coding!

[{“id”:“71315098.8e217”,“type”:“exec”,“z”:“61436644.968078”,“command”:“curl -k”,“addpay”:true,“append”:"",“useSpawn”:“false”,“timer”:"",“oldrc”:false,“name”:"",“x”:250,“y”:220,“wires”:[[“49b89c51.ea9344”],[“774de3f4.0de75c”],[“774de3f4.0de75c”]]},{“id”:“774de3f4.0de75c”,“type”:“debug”,“z”:“61436644.968078”,“name”:"",“active”:false,“console”:“false”,“complete”:“true”,“x”:490,“y”:220,“wires”:[]},{“id”:“924450dc.e1843”,“type”:“function”,“z”:“61436644.968078”,“name”:“set logging url”,“func”:"// The endpoint for your groov Trend pen to grab data from. \n// In the top-right corner of your groov pen, do a Right-click, Save Link as…\nreturn {\n payload: “”,\n// payload: “https://mary.groov.com/api/v0/data-logging/8075_0_1_1_false/scanned-data.csv?api_key=TjGs8bJ5PSmCxbLa",\n};",“outputs”:1,“noerr”:0,“x”:200,“y”:140,“wires”:[[“71315098.8e217”]]},{“id”:“55cc316.52124d”,“type”:“inject”,“z”:“61436644.968078”,“name”:"",“topic”:"",“payload”:"",“payloadType”:“date”,“repeat”:“60”,“crontab”:"",“once”:true,“x”:130,“y”:80,“wires”:[[“924450dc.e1843”]]},{“id”:“49b89c51.ea9344”,“type”:“split”,“z”:“61436644.968078”,“name”:"",“splt”:",",“spltType”:“str”,“arraySplt”:“13”,“arraySpltType”:“len”,“stream”:false,“addname”:"",“x”:410,“y”:160,“wires”:[[“ef2c02c3.9ebdc”]]},{“id”:“f7c4f5a0.f46898”,“type”:“debug”,“z”:“61436644.968078”,“name”:"",“active”:true,“console”:“false”,“complete”:“false”,“x”:770,“y”:360,“wires”:[]},{“id”:“82b91fe8.4a534”,“type”:“function”,“z”:“61436644.968078”,“name”:"One Minute Total”,“func”:"\nOneMinuteTotal = 0;\nFiveSecondValue = 0;\n\n// pick the values out of the csv (skipping the header & timestamps)\nfor (i = 3; i <= 25; i += 2){\n \n FiveSecondValue = Number(msg.payload[i]);\n if ( FiveSecondValue >= 0 ) // make sure it’s valid, whatever that means\n {\n OneMinuteTotal += FiveSecondValue;\n }\n else\n {\n // handle the bogus number, perhaps email someone…\n }\n}\n\nmsg.payload = OneMinuteTotal; \nreturn msg;\n\n",“outputs”:1,“noerr”:0,“x”:650,“y”:260,“wires”:[[“f7c4f5a0.f46898”]]},{“id”:“ef2c02c3.9ebdc”,“type”:“split”,“z”:“61436644.968078”,“name”:"",“splt”:"\n",“spltType”:“str”,“arraySplt”:“13”,“arraySpltType”:“len”,“stream”:false,“addname”:"",“x”:530,“y”:160,“wires”:[[“d7b68982.61f038”]]},{“id”:“d7b68982.61f038”,“type”:“join”,“z”:“61436644.968078”,“name”:"",“mode”:“custom”,“build”:“array”,“property”:“payload”,“propertyType”:“msg”,“key”:“topic”,“joiner”:"\n",“joinerType”:“str”,“accumulate”:false,“timeout”:"",“count”:“27”,“x”:650,“y”:160,“wires”:[[“82b91fe8.4a534”,“b011704a.118a8”]]},{“id”:“b011704a.118a8”,“type”:“debug”,“z”:“61436644.968078”,“name”:"",“active”:false,“console”:“false”,“complete”:“false”,“x”:830,“y”:160,“wires”:[]},{“id”:“c1f7c726.091e08”,“type”:“comment”,“z”:“61436644.968078”,“name”:“Set up the endpoint for your Trend’s pen’s API”,“info”:"",“x”:190,“y”:40,“wires”:[]},{“id”:“4c50aa2.6d06d54”,“type”:“comment”,“z”:“61436644.968078”,“name”:“grab that csv data”,“info”:"",“x”:238,“y”:275,“wires”:[]},{“id”:“ee2a553e.0c8028”,“type”:“comment”,“z”:“61436644.968078”,“name”:“slit on on comma and CR to get 26 array elements”,“info”:"// 2 header elements: “timestamp”, “value”\n// plus 12 x 5 second samples for 1 minute",“x”:530,“y”:120,“wires”:[]},{“id”:“6978aa58.cc26a4”,“type”:“comment”,“z”:“61436644.968078”,“name”:“loop through the pen values pulled from the csv”,“info”:"",“x”:800,“y”:220,“wires”:[]}]


A few things to keep in mind here:

  • Our scanner runs once a second, regardless of what your trend’s scan interval is set to. It’s a hard coded interval from the pre-1.0 days that we haven’t taken the time yet to go and fix. For trends we throw out values we don’t need. More trends == more load on your devices.
  • That said, we won’t scan the same value twice, so if you’re doing this for a tag you’re already using on a trend somewhere, there’s no extra load on the device.
  • Any value read into a trend is promoted to a 64-bit floating point value. Depending on your data, that might cause a weird coercion that you don’t want.
  • I’m somewhat loathe to mention this (it’s not public and is subject to change), but try changing the extension in the URL from csv to json. It might give you something you like more than a csv file.
  • Values in the JSON format will come through as strings to get around JSON not supporting NaN/Infinity.