Use RestAPI for groov EPIC to see if latest firmware is being used?

In Grafana, I use the Infinity datasource plugin to do the RestAPI call to our EPICs to display various info, such as the firwmare version, date, & hardware:

I’d like to make it so if there was a newer version available (e.g. R3.7), then it would display that. AFAIK, there is no such ability using the RestAPI to grab this info, but perhaps the latest firmware version & date are published elsewhere so that I could simply do a comparison between my current version and the latest available version?

Maybe there is a better way to do this instead of the RestAPI?

If I understand, you already get the current firmware from the EPIC/RIO, and you’d like to scrape our website for the current released firmware version and then do a field in Grafana that shows what’s on the website so you can see what’s running vs. what’s available?

Yes, that’s correct. And if I get fancy, if I’m using the latest version, my version will display in green and if there is an update, my version will appear in red.

If you don’t already have it, install to your pallet the string node:

Import and deploy this flow to make sure its working for you:

I’d do it once a day, no need to pull it more often, so change the inject from the current manual to a time.

This is what the little flow will return:

image

I will leave it to you on how you mash up the rest API call from the EPIC/RIO with this payload string.
There is no reason why this could not be run on each EPIC/RIO. (Of course, they need to have a valid gateway so they can access the Opto22 website.)

[
    {
        "id": "b22bc93bfedbf6e3",
        "type": "http request",
        "z": "9f2e5ad1b122eb5b",
        "name": "EPIC firmware page",
        "method": "GET",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "https://www.opto22.com/support/resources-tools/downloads/groov-epic-processor-firmware",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 1280,
        "y": 360,
        "wires": [
            [
                "c2e57f94e7377940"
            ]
        ]
    },
    {
        "id": "c2e57f94e7377940",
        "type": "string",
        "z": "9f2e5ad1b122eb5b",
        "name": "get firmware version string",
        "methods": [
            {
                "name": "getRightMost",
                "params": [
                    {
                        "type": "str",
                        "value": "<h1>groov EPIC Processor Firmware</h1>"
                    }
                ]
            },
            {
                "name": "between",
                "params": [
                    {
                        "type": "str",
                        "value": "Version: "
                    },
                    {
                        "type": "str",
                        "value": "</"
                    }
                ]
            }
        ],
        "prop": "payload",
        "propout": "payload",
        "object": "msg",
        "objectout": "msg",
        "x": 1540,
        "y": 360,
        "wires": [
            [
                "847633e27967fd29"
            ]
        ]
    },
    {
        "id": "847633e27967fd29",
        "type": "debug",
        "z": "9f2e5ad1b122eb5b",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1750,
        "y": 360,
        "wires": []
    },
    {
        "id": "308b87df14238f0c",
        "type": "inject",
        "z": "9f2e5ad1b122eb5b",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 1080,
        "y": 360,
        "wires": [
            [
                "b22bc93bfedbf6e3"
            ]
        ]
    }
]

EDIT: I have used this trick in the past, so here is how I go about it…
Right click the webpage you want the data from.
Select ‘view source’.
Do a ctrl + F and find the data you want.
Look above it for something very unique in the HTML. Do a search on that term to make sure its only showing up the once in the page.
Put that in the top part of the string node.
Then now that you have jumped to the part of the page that is of interest, you can use the tags/code etc around the data you want to zoom in and get the good stuff.

Hi Grant,
I have an old Pac Control chart from Mary that will Scape a webpage looking for specific strings in the html. I used it at one time so I know it works.
If using a strategy to do this fits into your scheme, I’d be happy to see if I can modify it to pull the firmware version off the website.
The downside is that you are at the mercy of web site changes.

@dp_engsberg Makes a really good point about website changes.

I don’t know of any other public-facing way to get the current firmware version.
You can use the same Node-RED flow (or PAC Control chart) to get the RIO firmware version off its web page.

Perhaps Opto could consider publishing a JSON page on their website that would allow reliable programmatic access to some of this information? Publishing firmware versions for controllers, modules, etc. could be very helpful, and publishing a JSON page would certainly make things easier than having the website scraped.

4 Likes

This is not perfect, but it’s closer to what I envisioned…

image

The top panel is coming from Ben’s flow, but I took it further and published it to an HTTP endpoint (192.168.10.85:1880/epic/latest_firmware) on our LAN, like this (am sure this is a cleaner way to do this)…

In Grafana, I used the Infinity datasource to hit the above endpoint:

and used UQL to hone in on what I wanted.

1 Like

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.

2 Likes