Customer to add LoRa devices to Gateway over a custom browser UI

Hi, has anyone tried this?

Raspberry Pi LoRaWAN Gateway (Chirpstack NS, AS)--------LAN--------Customer Local PC Browser url, purpose made dashboard UI.

The customer has the Gateway up and running and is monitoring device parameters locally on PC browser over custom made Dashboard User Interface.

We need to allow customer to add more LoRa devices to the Gateway over a custom browser UI.

Now as System Integrators we know that we can log into the AS and add devices with OTAA, devEUI and AppKey. But we want the customer to add new devices locally (without logging into Gateway AS) using purpose made Dashboard UI on browser by simply inserting Application name, devEUI, AppKey on a "Add Device"browser widget and simply clicking a button.

Is this possible? and if so, then want to know what is the path where Chirpstack saves the Application name, devEUI and AppKey on the Gateway?

Making a purpose made UI is not an issue, just want to know the path for saving Application name, devEUI, AppKey on the Gateway once the customer enters them on the purpose made UI, enters Application name, devEUI, AppKey for the new device and clicks “Add Application”, “Add Device”, “Delete Application”, “Delete Device”.

Sorry but I can not understand your issue. :upside_down_face:

please try to express it in a more clear way.

Maybe @brocaar can help you.

I’m not sure if I understand it fully either, but it sounds like you want to build something on top of the ChirpStack API?

@brocaar @NicolasUy

Allow me to expand my topic a little more.

I have a gateway with built-in Chirpstack API. I can add remove devices on it by logging in, no problem.

Now let us say if the gateway is not with me but with my customer (user) with some devices which are up and running. They would like to add a new device on the same gateway.

Raspberry Pi LoRaWAN Gateway (Chirpstack NS, AS)--------LAN--------Customer Local PC Browser url, purpose made dashboard UI Node-RED.

I also have Node-RED on the same gateway. I want my customer to add or remove devices by himself without using/logging into the resident Chirpstack API.

I can configure a dashboard using Node-RED which can be accessed by the customer over the browser http://IPAddress:1880/ui. Let us say if I make a purpose made Node-RED page on which I have widgets to allow my customer to input credentials for adding new application and device or deleting an existing application and/or device (without logging into the Chirpstack API).

In simple terms I want to add or remove devices using purpose made Node-Red UI by inputting Application name, devEUI, AppKey on the browser page and then click Save. Where does Chirpstack API save the Application name, devEUI, AppKey on the host Raspberry Pi (what is the file path)?

i think you are over complicating your life… It also sounds as if you have everything running on the pi?

You either need to:

  1. develop something against the API (your chirpstack ip/API)
  2. Use a excel to API → Lora App Server new device registration - #12 by fmgst
  3. Simply show the customer how to do it in the AS.

I would however not really look to run this all on a RPI… SD cards are not realy meant for this. You either need to get your own version of Chirpstack running in the cloud and then give him an account, or build a sull appliance install for him…

But running everything on an RPI is only going to cause you headaches…

Not sure if I have understood correctly from the reply and if I am on the right path??? Did a bit of study on the web to arrive at below:-

Should I use REST API, POST api/devices details using Node-RED http response and function node to send JSON object with Bearer =APIToken= authentication to the AS? Will it register a new device in this manner?

{
“device”: {
“applicationID”: “string”,
“description”: “string”,
“devEUI”: “string”,
“deviceProfileID”: “string”,
“name”: “string”,
“referenceAltitude”: 0,
“skipFCntCheck”: true,
“tags”: {},
“variables”: {}
}
}

Am I on the right path? A Node-RED flow example please would make it easier since I am not too familiar with such coding methods.

@Controlwiz I am also looking to do something similar using node-red, for our application we are trying to do a bulk device import into Chirpstack using the API and a .csv file.
I wouldn’t mind a interface for users to add their own devices too in the future.
I’d be happy to share my flow if I can accomplish that.

@Rosty, unfortunately haven’t achieved that function yet. Still want guidance on it. Let me revive the thread here again as a subject, perhaps someone can assist.

Aim: An end-user has a set of Standalone LoRaWAN Gateway (localhost, GW, LNS and AS and Node-RED) and some LoRaWAN node devices already working fine. Further is there a way the end-user himself can add more devices/nodes over the Node-RED web interface on the Gateway? i.e without logging in to the Application Server???

1 Like

@Controlwiz This is what I created for our use. I am not allowing end-users manage the system though, I assume you’ll want to make some changes to this flow, but I hope it gives you a good starting point.
[ { "id": "9f80a9ba6f93914d", "type": "inject", "z": "bd0d0f5f3bdcae7d", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 200, "y": 340, "wires": [ [ "f345f9742a9f75eb" ] ] }, { "id": "2b6bd82fafb1da3c", "type": "group", "z": "bd0d0f5f3bdcae7d", "style": { "stroke": "#999999", "stroke-opacity": "1", "fill": "none", "fill-opacity": "1", "label": true, "label-position": "nw", "color": "#a4a4a4" }, "nodes": [ "4e4943ea0df5c326", "a7fd77e723a2cc7b", "ea79b225fea62c08", "0db5093301817c23", "5865fd44bd88c99b", "b985da91e6bd7f7b", "021f7e05d71a67ce" ], "x": 814, "y": 159, "w": 912, "h": 182 }, { "id": "4e4943ea0df5c326", "type": "http request", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "DEVICE", "method": "POST", "ret": "txt", "paytoqs": "ignore", "url": "http://CHIRPSTACK IP:8090/api/devices", "tls": "", "persist": true, "proxy": "", "insecureHTTPParser": false, "authType": "bearer", "senderr": false, "headers": [], "x": 1480, "y": 300, "wires": [ [ "a7fd77e723a2cc7b" ] ] }, { "id": "a7fd77e723a2cc7b", "type": "debug", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1630, "y": 300, "wires": [] }, { "id": "ea79b225fea62c08", "type": "function", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "createHeaders", "func": "msg.headers = {};\nmsg.headers['Content-Type'] = 'application/json';\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['Grpc-Metadata-Authorization'] = 'Bearer Token Here'\nreturn msg;", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1320, "y": 300, "wires": [ [ "4e4943ea0df5c326" ] ] }, { "id": "0db5093301817c23", "type": "template", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "", "field": "payload", "fieldType": "msg", "format": "handlebars", "syntax": "mustache", "template": "{\n \"device\": {\n \"applicationId\": \"Insert Application ID Here\",\n \"description\": \"Installed at PWC\",\n \"devEui\": \"{{deveui}}\",\n \"deviceProfileId\": \"Insert Device Profile ID Here\",\n \"isDisabled\": false,\n \"joinEui\": \"{{join}}\",\n \"name\": \"{{name}}\",\n \"skipFcntCheck\": false,\n \"tags\": {},\n \"variables\": {}\n }\n}\n\n", "output": "str", "x": 1160, "y": 300, "wires": [ [ "ea79b225fea62c08" ] ] }, { "id": "5865fd44bd88c99b", "type": "csv", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "", "sep": ",", "hdrin": true, "hdrout": "", "multi": "one", "ret": "\\n", "temp": "brand,sensor_name,serial_number,dev_eui,app_eui,app_key,dev_address,nets_key,apps_key", "skip": "0", "strings": true, "include_empty_strings": false, "include_null_values": false, "x": 890, "y": 300, "wires": [ [ "b985da91e6bd7f7b" ] ] }, { "id": "b985da91e6bd7f7b", "type": "function", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "", "func": "msg.name = msg.payload.sensor_name\nmsg.deveui = msg.payload.dev_eui\nmsg.desc = msg.payload.serial_number\nmsg.join = msg.payload.app_eui\nmsg.key = msg.payload.app_key\nreturn msg;\n", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1020, "y": 300, "wires": [ [ "0db5093301817c23" ] ] }, { "id": "021f7e05d71a67ce", "type": "comment", "z": "bd0d0f5f3bdcae7d", "g": "2b6bd82fafb1da3c", "name": "Device Import - DO 1st", "info": "", "x": 940, "y": 200, "wires": [] }, { "id": "1c67b18e94a90c4e", "type": "group", "z": "bd0d0f5f3bdcae7d", "style": { "stroke": "#999999", "stroke-opacity": "1", "fill": "none", "fill-opacity": "1", "label": true, "label-position": "nw", "color": "#a4a4a4" }, "nodes": [ "f345f9742a9f75eb", "15ecef12af68c479", "d0356427e030d206" ], "x": 274, "y": 159, "w": 532, "h": 382 }, { "id": "f345f9742a9f75eb", "type": "file in", "z": "bd0d0f5f3bdcae7d", "g": "1c67b18e94a90c4e", "name": "IMPORT", "filename": "/home/nodered/30034.csv", "filenameType": "str", "format": "utf8", "chunk": false, "sendError": false, "encoding": "none", "allProps": false, "x": 540, "y": 320, "wires": [ [ "5865fd44bd88c99b" ] ] }, { "id": "15ecef12af68c479", "type": "file in", "z": "bd0d0f5f3bdcae7d", "g": "1c67b18e94a90c4e", "name": "TEST", "filename": "/home/nodered/test.csv", "filenameType": "str", "format": "utf8", "chunk": false, "sendError": false, "encoding": "none", "allProps": false, "x": 530, "y": 500, "wires": [ [] ] }, { "id": "d0356427e030d206", "type": "comment", "z": "bd0d0f5f3bdcae7d", "g": "1c67b18e94a90c4e", "name": ".CSV Files - Change output between Device import and Key Import", "info": "", "x": 540, "y": 200, "wires": [] }, { "id": "cbb3a545926cac28", "type": "group", "z": "bd0d0f5f3bdcae7d", "style": { "stroke": "#999999", "stroke-opacity": "1", "fill": "none", "fill-opacity": "1", "label": true, "label-position": "nw", "color": "#a4a4a4" }, "nodes": [ "d86e5dd8c85262b1", "aa0fb1bd8d66d2cf", "3eb12c622bb02984", "b64de9927dfefa6d", "46cdf4deb874e794", "644108beb588d055", "7fab542dca98c43d" ], "x": 814, "y": 359, "w": 912, "h": 182 }, { "id": "d86e5dd8c85262b1", "type": "template", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "", "field": "payload", "fieldType": "msg", "format": "handlebars", "syntax": "mustache", "template": "{\n \"deviceKeys\": {\n \"nwkKey\": \"{{key}}\"\n }\n}", "output": "str", "x": 1160, "y": 500, "wires": [ [ "aa0fb1bd8d66d2cf" ] ] }, { "id": "aa0fb1bd8d66d2cf", "type": "function", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "createHeaders", "func": "msg.headers = {};\nmsg.headers['Content-Type'] = 'application/json';\nmsg.headers['Accept'] = 'application/json';\nmsg.headers['Grpc-Metadata-Authorization'] = 'Bearer Token Here'\n\nreturn msg;", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1320, "y": 500, "wires": [ [ "3eb12c622bb02984" ] ] }, { "id": "3eb12c622bb02984", "type": "http request", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "OTAA KEY", "method": "POST", "ret": "txt", "paytoqs": "ignore", "url": "http://CHIRPSTACK IP:8090/api/devices/{{{deveui}}}/keys", "tls": "", "persist": true, "proxy": "", "insecureHTTPParser": false, "authType": "bearer", "senderr": false, "headers": [], "x": 1490, "y": 500, "wires": [ [ "b64de9927dfefa6d" ] ] }, { "id": "b64de9927dfefa6d", "type": "debug", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 1630, "y": 500, "wires": [] }, { "id": "46cdf4deb874e794", "type": "csv", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "", "sep": ",", "hdrin": true, "hdrout": "", "multi": "one", "ret": "\\n", "temp": "brand,sensor_name,serial_number,dev_eui,app_eui,app_key,dev_address,nets_key,apps_key", "skip": "0", "strings": true, "include_empty_strings": false, "include_null_values": false, "x": 890, "y": 500, "wires": [ [ "644108beb588d055" ] ] }, { "id": "644108beb588d055", "type": "function", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "", "func": "msg.name = msg.payload.sensor_name\nmsg.deveui = msg.payload.dev_eui\nmsg.desc = msg.payload.serial_number\nmsg.join = msg.payload.app_eui\nmsg.key = msg.payload.app_key\nmsg.devaddr = msg.payload.dev_address\nmsg.nets = msg.payload.nets_key\nmsg.apps = msg.payload.apps_key\nreturn msg;\n", "outputs": 1, "timeout": "", "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 1020, "y": 500, "wires": [ [ "d86e5dd8c85262b1" ] ] }, { "id": "7fab542dca98c43d", "type": "comment", "z": "bd0d0f5f3bdcae7d", "g": "cbb3a545926cac28", "name": "OTAA Key Import - DO 2nd", "info": "", "x": 950, "y": 400, "wires": [] } ]

@Rosty - Brilliant. As I can gather from your example flow that you have demonstrated a method how to add a device from within the Node-RED portal. No doubt this is a good starting point of what we want to finally achieve.

Using your example flow as a trial, I created a csv file with the device details, gave a correct path to csv output file, added API bearer key on template as well as “http request node (OTAA KEY node)” and my port number as default 8080.

When I triggered input node, I got a response payload as " “{“error”:“object does not exist”,“code”:5,“message”:“object does not exist”,“details”:}”, statusCode:404. Not sure why? It does not add the device to the application. Not sure what element is incorrect on my attempt?

I do not see any insert key:value pair for Application ID under which the device should be added, could that be the reason? Have attached a debug snapshot.

404 is a Page Not Found error. Try putting 8090 back in instead of 8080. 8090 is the port for the API.

No. With 8090 it says ECONNREFUSED whereas with 8080 it at least says “device does not exist” or if a device is already configured using API then it says “device already exists”. May be there is something else. I do not see the API ID element on the nodes, perhaps.

You want to add LoRaWAN device to the ChirpStack (Application+Network server) in your standalone gateway.

The standalone gateway has embedded ChirpStack server inside.
And that may create confusion that you add devices into the gateway.

You need to double check the port of the ChirpStack server.
You need to check the url of your ChirpStack API.
http(s)://ip:port/swagger-ui/ or http(s)://ip:port/api/swagger-ui/

If you can see this page, then you can see the Create device.
Need to create application > create Device profile > create device.

@IoTThinks Ok, shall try that.