If anybody else is looking for a fairly quick way to do this, I set up a Node-RED flow that could be executed periodically (weekly?) to retrieve the latest firmware versions from Opto22’s website. Right now, I’m retrieving a list of versions for products I care about and simply printing a Javascript array to the debug window, but once the retrieval and formatting is done, you could send the latest version to MQTT, a database, a variable, etc.
This is a quick, down and dirty flow. No error checking. No request status code checking. Those things wouldn’t be too bad to add, but I didn’t do it (yet).
The data (msg.payload
) I’m retrieving looks like this in the end:
[
{
"product":"GRV-EPIC-PR1",
"version":"3.6.0"
},
{
"product":"GRV-R7-MM1001-10",
"version":"3.6.0"
},
{
"product":"GRV-OACI-12",
"version":"1.3d"
},
{
"product":"GRV-IMAI-8",
"version":"1.3f"
},
{
"product":"GRV-IRTD-8",
"version":"R1.3e"
},
{
"product":"GRV-IVI-12",
"version":"1.3f"
}
]
My flow has a function node in the beginning with a function block containing the definition of the list of products:
msg.firmware = [
{
"product": "GRV-EPIC-PR1",
"url": "https://www.opto22.com/support/resources-tools/downloads/groov-epic-processor-firmware",
"version": null
},
{
"product": "GRV-R7-MM1001-10",
"url": "https://www.opto22.com/support/resources-tools/downloads/grv-r7-mm1001-10-firmware",
"version": null
},
{
"product": "GRV-OACI-12",
"url": "https://www.opto22.com/support/resources-tools/downloads/grv-oaci-12-module-firmware",
"version": null
},
{
"product": "GRV-IMAI-8",
"url": "https://www.opto22.com/support/resources-tools/downloads/grv-imai-8-module-firmware",
"version": null
},
{
"product": "GRV-IRTD-8",
"url": "https://www.opto22.com/support/resources-tools/downloads/grv-irtd-8-module-firmware",
"version": null
},
{
"product": "GRV-IVI-12",
"url": "https://www.opto22.com/support/resources-tools/downloads/grv-ivi-12-module-firmware",
"version": null
},
];
return msg;
Here is the Node-RED flow:
[{"id":"5e5fd22fa49556f8","type":"tab","label":"Latest Firmware","disabled":false,"info":"","env":[]},{"id":"eaa269a99255576d","type":"function","z":"5e5fd22fa49556f8","name":"initialize","func":"msg.firmware = [\n {\n \"product\": \"GRV-EPIC-PR1\",\n \"url\": \"https://www.opto22.com/support/resources-tools/downloads/groov-epic-processor-firmware\",\n \"version\": null\n },\n {\n \"product\": \"GRV-R7-MM1001-10\",\n \"url\": \"https://www.opto22.com/support/resources-tools/downloads/grv-r7-mm1001-10-firmware\",\n \"version\": null\n },\n {\n \"product\": \"GRV-OACI-12\",\n \"url\": \"https://www.opto22.com/support/resources-tools/downloads/grv-oaci-12-module-firmware\",\n \"version\": null\n },\n {\n \"product\": \"GRV-IMAI-8\",\n \"url\": \"https://www.opto22.com/support/resources-tools/downloads/grv-imai-8-module-firmware\",\n \"version\": null\n },\n {\n \"product\": \"GRV-IRTD-8\",\n \"url\": \"https://www.opto22.com/support/resources-tools/downloads/grv-irtd-8-module-firmware\",\n \"version\": null\n },\n {\n \"product\": \"GRV-IVI-12\",\n \"url\": \"https://www.opto22.com/support/resources-tools/downloads/grv-ivi-12-module-firmware\",\n \"version\": null\n },\n];\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":80,"wires":[["fc6e8a3e8b469b62"]]},{"id":"e95f12928a023bb6","type":"inject","z":"5e5fd22fa49556f8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":80,"wires":[["eaa269a99255576d"]]},{"id":"fc6e8a3e8b469b62","type":"loop","z":"5e5fd22fa49556f8","name":"retrieve each","kind":"enum","count":"","initial":"1","step":"1","condition":"","conditionType":"js","when":"before","enumeration":"firmware","enumerationType":"msg","limit":"","loopPayload":"loop-index","finalPayload":"final-last","x":470,"y":80,"wires":[["5f09f09a69d0e5aa"],["def653caddbb3355"]]},{"id":"b2d748a63945c7e9","type":"http request","z":"5e5fd22fa49556f8","name":"get webpage","method":"GET","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":470,"y":180,"wires":[["658acc8c699df553"]]},{"id":"def653caddbb3355","type":"function","z":"5e5fd22fa49556f8","name":"format request","func":"msg.loopIndex = msg.payload;\nmsg.url = msg.firmware[msg.loopIndex].url;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":140,"wires":[["b2d748a63945c7e9"]]},{"id":"f986c0bf55da08b0","type":"debug","z":"5e5fd22fa49556f8","name":"payload","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":840,"y":80,"wires":[]},{"id":"658acc8c699df553","type":"function","z":"5e5fd22fa49556f8","name":"extract version","func":"const regex = />Version:\\s*(\\w+(\\.\\w+)+)<\\/span>/;\nlet matches = regex.exec(msg.payload);\nmsg.firmware[msg.loopIndex].version = matches[1];\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":240,"wires":[["ff7516069168d936"]]},{"id":"5f09f09a69d0e5aa","type":"function","z":"5e5fd22fa49556f8","name":"cleanup","func":"msg.payload = msg.firmware;\ndelete msg.loopIndex;\ndelete msg.url;\ndelete msg.firmware;\ndelete msg.loop;\ndelete msg.statusCode;\ndelete msg.responseUrl;\ndelete msg.redirectList;\ndelete msg.retry;\ndelete msg.responseCookies;\ndelete msg.headers;\nmsg.payload.forEach(obj => {\n delete obj.url;\n});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":80,"wires":[["f986c0bf55da08b0"]]},{"id":"687dc94cfd1582a8","type":"link in","z":"5e5fd22fa49556f8","name":"loop","links":["ff7516069168d936"],"x":415,"y":40,"wires":[["fc6e8a3e8b469b62"]]},{"id":"ff7516069168d936","type":"link out","z":"5e5fd22fa49556f8","name":"loop","mode":"link","links":["687dc94cfd1582a8"],"x":415,"y":280,"wires":[]}]
The flow does use a node from node-red-contrib-loop
, so that needs to be installed in order to run this flow.