Application
The following scripts show you how to build a basic application onto the LoRaWAN network of the NOI Techpark. Please, choose the programming language you are more comfortable with between python or Java.
python
At first, you'll need to set your environment: (you can try to skip one or more steps at your own risk)
- install PyCharm or IntelliJ IDEA;
- install Python 3.7;
- create a python project in you IDE (please, make sure to select the python interpreter as Existing Interpreter);
- create a new python file (File → New → Python File → hack.py);
- copy and paste the following script into the hach.py file;
- replace DEV EUI, USERNAME, PASSWORD, DB NAME inside the script;
- run the code through right click on the script and select Run hack.
Now, this basic python web server should be querying the InfluxDB database and sending back a downlink to the node through the LoRaServer API. You can simply modify the last loop in the script to build your own application.
"""Python server consuming LoRaServer and InfluxDB APIs
Copyright (C) 2018 Eurac Research - Center for Sensing Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA"""
import requests
import warnings
import time
import json
# Global variables
loraAPI_url = "http://saocompute.eurac.edu/LoRaApp/api/"
influxAPI_url = "http://saocompute.eurac.edu/sensordb/query"
database = "XXXXXXXXXX" # the database name here
username = "XXXXXXXXXX" # your username here
password = "XXXXXXXXXX" # your password here
dev_EUI = "XXXXXXXXXX" # your device EUI here
TIME_SLEEP = 5
# JWT generation method needed to query the LoRa App Server API
def regenerateJWT(password,username):
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
url = loraAPI_url + 'internal/login'
data = {"password": password, "username": username}
response = postRequest(url, headers, data)
print(response.content)
if (response.status_code == 200):
new_jwt = json.loads(response.content.decode())['jwt']
return new_jwt
else:
print("Problems in generating a new JWT")
return -1
# Downlink building method
def sendDownlink(jwt, message):
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Grpc-Metadata-Authorization': 'Bearer ' + jwt
}
url = loraAPI_url + 'devices/' + dev_EUI + '/queue'
print(url)
data = {"deviceQueueItem": {"confirmed": True, "devEUI": dev_EUI,
"fCnt": getFrameUplink(jwt), "fPort": 2, "jsonObject": message}}
response = postRequest(url, headers, data)
if (response.status_code == 200):
print("Message sent")
else:
print(response.content)
print("Problems in sending message")
# Uplink-Downlink synchronization method
def getFrameUplink(jwt):
headers = {
'Accept': 'application/json',
'Grpc-Metadata-Authorization': 'Bearer ' + jwt
}
url = loraAPI_url + 'devices/' + dev_EUI + '/activation'
response = getRequest(url, headers)
frameuplink = json.loads(response.content.decode())['deviceActivation']['fCntUp']
if (response.status_code == 200):
#print("Frame uplink retrieved successfully")
return frameuplink
else:
print("Problem in retrieving the frame uplink.")
return 0
# GET request method
def getRequest(url, headers):
return requests.get(url, verify=False, headers=headers)
# POST request method
def postRequest(url, headers, data):
return requests.post(url, verify=False, headers=headers, json=data)
# Main body
warnings.filterwarnings("ignore")
current_jwt = regenerateJWT(password,username)
database = "?db="+database
username = "&u="+username
password = "&p="+password
first_loop = True
old_value = ""
if(current_jwt!=-1):
while (True):
warnings.filterwarnings("ignore")
# Query that retrieves the last value stored into the InfluxDB database
base_url = influxAPI_url + database + username + password
query_url = "SELECT device_name, value FROM device_frmpayload_data_message WHERE dev_eui='" + dev_EUI + "' ORDER BY time DESC LIMIT 1"
final_url = base_url + "&q=" + query_url
response = requests.get(final_url)
jsonresult = json.loads(response.content.decode())['results'][0]['series'][0]
print("DEV_NAME " + jsonresult['values'][0][1])
value = jsonresult['values'][0][2]
print("VALUE_STORED " + value)
if(first_loop):
old_value = value
first_loop = False
if(value != old_value):
downlink_msg = str('{"message":"'+value+'"}')
print(downlink_msg)
sendDownlink(current_jwt, downlink_msg)
old_value = value
else:
print("Same value")
print("----------------------------------------------------------")
time.sleep(TIME_SLEEP)
else:
print("Some problem occurred... exiting the script")
Note
If the script doesn't work, please try to:
- edit the configuration settings (Run → Edit Configurations). Here, check if the Script path is set to the name of your python script.
- check if your anti-virus is blocking the python script. In case, make it allowing the script and run the server one more time.
If you are more likely to consume the InfluxDB and the LoRaServer APIs as they are, please go to the section APIs.
Java
Before to start, set your environment as follows: (you can skip one or more steps at your own risk)
- install Eclipse or IntelliJ IDEA;
- install Java JDK 1.8;
- create a new Java project named hack;
- download the JSON .jar file and add it to the dependencies of the project;
- look at this guide on how to use InfluxDB in a Java environment.
The scripts below pretend to be a basic Java web server. It queries the InfluxDB database and builds the downlinks to be sent back to the node through the LoRaServer API.
Remember to replace DEV EUI, USERNAME, PASSWORD, and DB NAME in the code.
"""Java server consuming LoRaServer and InfluxDB APIs
Copyright (C) 2018 Eurac Research - Center for Sensing Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA"""
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
public class hack_server {
// Global variables
public static String influxAPI_url = "http://saocompute.eurac.edu/sensordb/query"; // influx URL API
private static String database = "XXXXXXXXXX"; // the database name here
private static String username = "XXXXXXXXXX"; // the username here
private static String password = "XXXXXXXXXX"; // the password here
private static String dev_EUI = "XXXXXXXXXX"; // the device EUI goes here
public static void main(String[] args) throws Exception {
JSONParser jsonParser = new JSONParser();
LoRaAPI l = new LoRaAPI(jsonParser, username, password);
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
String jwt = l.generateJWT(allHostsValid);
String old_value = "";
boolean first_loop = true;
if(!jwt.equals("no_jwt")) {
database = "?db="+database;
username = "&u="+username;
password = "&p="+password;
while (true) {
String base_url = influxAPI_url + database + username + password;
String query_url = "SELECT device_name, value FROM device_frmpayload_data_message WHERE dev_eui='"+ dev_EUI + "' ORDER BY time DESC LIMIT 1";
String restUrl = URLEncoder.encode(query_url, "UTF-8");
URL final_url = new URL(base_url + "&q=" + restUrl);
HttpURLConnection conn = (HttpURLConnection) final_url.openConnection();
conn.setRequestMethod("GET");
Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String outputGetRequest = ((BufferedReader) in).readLine();
JSONObject outputJson = (JSONObject) jsonParser.parse(outputGetRequest);
JSONObject currentJson = (JSONObject) ((JSONArray) outputJson.get("results")).get(0);
JSONObject currentSerie = (JSONObject) ((JSONArray) currentJson.get("series")).get(0);
JSONArray values = (JSONArray) currentSerie.get("values");
String device_name = ((JSONArray) values.get(0)).get(1).toString();
String value = ((JSONArray) values.get(0)).get(2).toString();
System.out.println("DEV_NAME " + device_name);
System.out.println("VALUE_STORED " + value);
if(first_loop){
old_value = value;
first_loop = false;
}
if(!value.equals(old_value)) {
String downlink_msg = "{\"message\":\""+value+"\"}";
System.out.println(downlink_msg);
l.sendInstructions(allHostsValid, downlink_msg, dev_EUI);
old_value = value;
} else {
System.out.println("Same value");
}
System.out.println("----------------------------------------------------------");
TimeUnit.SECONDS.sleep(5);
}
} else {
System.out.print("Some problem occurs... exiting the script");
}
}
}
Here follows the LoRaAPI class which implements the methods to consume the LoRaServer API.
"""Java class consuming LoRaServer and InfluxDB APIs
Copyright (C) 2018 Eurac Research - Center for Sensing Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA"""
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
public class LoRaAPI {
private static String loraAPI_url = "http://saocompute.eurac.edu/LoRaApp/api/";
private String username;
private String password;
private JSONParser jsonParser;
private String JWT;
public LoRaAPI(JSONParser jsonParser, String username, String password) {
this.jsonParser = jsonParser;
this.username = username;
this.password = password;
}
public String getJWT() {
return JWT;
}
public void setJWT(String JWT) {
this.JWT = JWT;
}
// Method to generate the JWT needed to connect to the LoRaServer API
public String generateJWT(HostnameVerifier allHostsValid) throws Exception{
JSONObject loginData = new JSONObject();
loginData.put("password", password);
loginData.put("username", username);
URL url = new URL(loraAPI_url+"internal/login");
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpURLConnection conn = this.postRequest(url,loginData.toString(),allHostsValid);
int resp = conn.getResponseCode();
if(resp==200){
Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String outputGetRequest = ((BufferedReader) in).readLine();
JSONObject currentJson = (JSONObject) jsonParser.parse(outputGetRequest);
String newJWT = (String) currentJson.get("jwt");
setJWT(newJWT);
return newJWT;
} else {
System.out.println("There's a problem generating JWT");
return "no_jwt";
}
}
// Method to send an acknowledgment back to the node
public void sendInstructions(HostnameVerifier allHostsValid, String instruction, String dev_EUI) throws Exception {
String payload = formatDeviceInstructions(dev_EUI,allHostsValid, instruction).toString();
URL url = new URL(loraAPI_url + "devices/" + dev_EUI + "/queue");
HttpURLConnection conn = this.postRequest(url,payload,allHostsValid);
int resp = conn.getResponseCode();
if(resp==200){
System.out.println("Message sent");
Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
} else {
System.out.println(conn.getErrorStream());
System.out.println("There's a problem sending the message");
}
}
// Method to create the JSONObject with the instructions to be forwarded to the node
public JSONObject formatDeviceInstructions(String dev_EUI, HostnameVerifier allHostsValid, String instruction) throws Exception {
JSONObject devQueue = new JSONObject();
JSONObject devQueueItem = new JSONObject();
devQueueItem.put("confirmed",true);
devQueueItem.put("devEUI", dev_EUI);
devQueueItem.put("fCnt", getFrameUplink(dev_EUI,allHostsValid));
devQueueItem.put("fPort", 1);
devQueueItem.put("jsonObject", instruction);
devQueue.put("deviceQueueItem",devQueueItem);
return devQueue;
}
// Method to get the counter of uplink frames of the node (needed to synchronize the uplink/downlink handshake)
public int getFrameUplink(String dev_EUI, HostnameVerifier allHostsValid) throws Exception {
URL url = new URL(loraAPI_url+ "devices/" + dev_EUI + "/activation");
HttpURLConnection conn = this.getRequest(url,allHostsValid);
int resp = conn.getResponseCode();
if(resp==200){
Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String outputGetRequest = ((BufferedReader) in).readLine();
JSONObject currentJson = (JSONObject) jsonParser.parse(outputGetRequest);
JSONObject singleObj = (JSONObject) currentJson.get("deviceActivation");
int fCntUp = Integer.parseInt(singleObj.get("fCntUp").toString());
return fCntUp;
} else {
System.out.println("There's a problem retrieving the frame uplink");
return -1;
}
}
// Method to do a POST request to the LoRaServer API
public HttpURLConnection postRequest(URL url, String payload, HostnameVerifier allHostsValid) throws Exception {
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Authorization", "Bearer " + getJWT());
conn.setDoOutput(true);
conn.getOutputStream().write(payload.getBytes("UTF-8"));
return conn;
}
// Method to do a GET request to the LoRaServer API
public HttpURLConnection getRequest(URL url, HostnameVerifier allHostsValid) throws Exception {
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Authorization", "Bearer " + getJWT());
return conn;
}
}
If you are more likely to consume the InfluxDB and the LoRaServer APIs as they are, please look at the section APIs.
APIs
InfluxDB API
Here, the link to the official documentation of the InfluxDB API.
LoRa App Server API
Proceed on this link if you want directly access the LoRa App Server API.
Note
- Some methods won't be available according to your permissions.
- Remember that you are not allowed to send uplinks more often than every 10 seconds.
This work is licensed under a Creative Commons Attribution 4.0 International License.