Feat: Integrating Dragino lps8n

This commit is contained in:
Halcy0nic
2024-01-31 11:29:52 -07:00
parent cf85109bbd
commit e4594ae664
8 changed files with 2108 additions and 51 deletions

89
app.py
View File

@@ -3,10 +3,12 @@ from markupsafe import escape
from flask_socketio import SocketIO, emit
import serial
import threading
from threading import Timer
import time
from collections import deque
import pandas as pd
import re
import requests
from bs4 import BeautifulSoup
app = Flask(__name__)
socketio = SocketIO(app)
@@ -18,7 +20,6 @@ port3_status = True
global ser1
global ser2
global ser3
global_dataframe = pd.DataFrame(columns=['Device Name', 'Frequency', 'Signal Strength', 'Plaintext'])
frequency = lambda port: {'port1': 433, 'port2': 868,'port3': 915}.get(port, None)
surveydata = {}
@@ -75,8 +76,78 @@ def read_serial_data(port, ser, buffer):
print(f"Error: {e}")
pass
def parse_and_store_data():
global surveydata
url = "http://10.130.1.1/cgi-bin/log-traffic.has" # Your target URL
headers = {
"Host": "10.130.1.1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"DNT": "1",
"Sec-GPC": "1",
"Authorization": "Basic cm9vdDpkcmFnaW5v",
"Connection": "keep-alive",
"Referer": "http://10.130.1.1/cgi-bin/log-lora.has",
"Upgrade-Insecure-Requests": "1"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
soup = BeautifulSoup(response.text, 'html.parser')
table = soup.find('table')
rows = table.find_all('tr')
headers = [header.text.strip() for header in rows[0].find_all('th')][1:]
for row in rows[1:]:
cells = row.find_all('td')
cell_data = [cell.text.strip() for cell in cells[1:] if cells.index(cell) < len(headers) + 1]
formatted_row = ' | '.join(cell_data)
# Extract DevEui or DevAddr from the response
dev_id = extract_dev_id(formatted_row) # Implement this function based on your data format
freq = extract_freq(formatted_row) # Implement this function based on your data format
# Initialize dictionary for dev_id if not present
if dev_id not in surveydata:
surveydata[dev_id] = []
# Append new data to the list associated with the DevEui or DevAddr
surveydata[dev_id].append([freq, 0, formatted_row])
#surveydata[dev_id]['decoded_values'].append(formatted_row)
print("Data parsed and stored.")
else:
print(f"Request failed with status code: {response.status_code}")
# Schedule the next call to this function
Timer(60, parse_and_store_data).start() # Call this function every 60 seconds
def extract_dev_id(formatted_row):
# Assuming DevEui or DevAddr is in the 'Content' part of the formatted_row
# and it's formatted like 'Dev Addr: {DevEui}, Size: {Size}'
try:
content_part = formatted_row.split('|')[-1].strip() # Get the last part of the formatted_row, which is 'Content'
dev_id = content_part.split(',')[0].split(':')[-1].strip() # Extract the DevEui or DevAddr
return dev_id
except Exception as e:
print(f"Error extracting DevEui/DevAddr: {e}")
return None # Return None or some default value if extraction fails
def extract_freq(formatted_row):
# Assuming 'Freq' is a standalone field in the formatted_row
try:
freq_part = formatted_row.split('|')[3].strip() # Get the 'Freq' part (assuming it's the fifth field)
freq = float(freq_part) # Convert the frequency to float
return freq
except Exception as e:
print(f"Error extracting frequency: {e}")
return None # Return None or some default value if extraction fails
def connect_serial(port,frequency):
@@ -144,7 +215,7 @@ def analysis():
@app.route('/survey')
def survey():
return render_template('survey.html', data=global_dataframe)
return render_template('survey.html')
@app.route('/tracking')
def tracking():
@@ -255,8 +326,16 @@ def checkSer():
@app.route('/get_table_data')
def get_table_data():
global surveydata
print(surveydata)
return jsonify(surveydata)
cleaned_data = {}
for dev_id, data in surveydata.items():
if dev_id: # Check if dev_id is not empty
cleaned_data[dev_id] = data
#print(cleaned_data) # For debugging
return jsonify(cleaned_data)
if __name__ == '__main__':
Timer(60, parse_and_store_data).start()
socketio.run(app, debug=True)

58
draginoReq.py Normal file
View File

@@ -0,0 +1,58 @@
import requests
from bs4 import BeautifulSoup
# Define the URL and headers
url = "http://10.130.1.1/cgi-bin/log-traffic.has"
headers = {
"Host": "10.130.1.1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:122.0) Gecko/20100101 Firefox/122.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"DNT": "1",
"Sec-GPC": "1",
"Authorization": "Basic cm9vdDpkcmFnaW5v",
"Connection": "keep-alive",
"Referer": "http://10.130.1.1/cgi-bin/log-lora.has",
"Upgrade-Insecure-Requests": "1"
}
# Send the GET request
response = requests.get(url, headers=headers)
if response.status_code == 200:
# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(response.text, 'html.parser')
# Find the table
table = soup.find('table')
# Initialize an empty list to store formatted strings for each row
formatted_rows = []
# Find all table rows
rows = table.find_all('tr')
# Get column headers from the first row
# Using .text.strip() to clean the text and [1:] to skip the empty first column
headers = [header.text.strip() for header in rows[0].find_all('th')][1:]
# Iterate through each row (skipping the first row with the headers)
for row in rows[1:]:
# Find all data cells (td tags) in the row
cells = row.find_all('td')
# Extract text from each cell
# Using [1:] to skip the first cell with the arrow icon
cell_data = [cell.text.strip() for cell in cells[1:] if cells.index(cell) < len(headers) + 1]
# Format the row data into a neat line
formatted_row = ' | '.join(cell_data)
# Append the formatted string to the list
formatted_rows.append(formatted_row)
# Print the formatted string to display the row
print(formatted_row)
else:
print("Request failed with status code:", response.status_code)

1767
examplereq Normal file

File diff suppressed because it is too large Load Diff

122
lorawan.py Normal file
View File

@@ -0,0 +1,122 @@
##!/usr/bin/env python3
import io
import sys
import time
import datetime
import argparse
from enum import IntEnum
import serial
from serial.threaded import LineReader, ReaderThread
parser = argparse.ArgumentParser(description='Connect to LoRaWAN network')
parser.add_argument('port', help="Serial port of LoStik")
parser.add_argument('--joinmode', '-j', help="otaa, abp", default="otaa")
# ABP credentials
parser.add_argument('--appskey', help="App Session Key", default="")
parser.add_argument('--nwkskey', help="Network Session Key", default="")
parser.add_argument('--devaddr', help="Device Address", default="")
# OTAA credentials
parser.add_argument('--appeui', help="App EUI", default="")
parser.add_argument('--appkey', help="App Key", default="")
parser.add_argument('--deveui', help="Device EUI", default="")
args = parser.parse_args()
OTAA_RETRIES = 5
class MaxRetriesError(Exception):
pass
class ConnectionState(IntEnum):
SUCCESS = 0
CONNECTING = 100
CONNECTED = 200
FAILED = 500
TO_MANY_RETRIES = 520
class PrintLines(LineReader):
retries = 0
state = ConnectionState.CONNECTING
def retry(self, action):
if(self.retries >= OTAA_RETRIES):
print("Too many retries, exiting")
self.state = ConnectionState.TO_MANY_RETRIES
return
self.retries = self.retries + 1
action()
def get_var(self, cmd):
self.send_cmd(cmd)
return self.transport.serial.readline()
def join(self):
if args.joinmode == "abp":
self.join_abp()
else:
self.join_otaa()
def join_otaa(self):
if len(args.appeui):
self.send_cmd('mac set appeui %s' % args.appeui)
if len(args.appkey):
self.send_cmd('mac set appkey %s' % args.appkey)
if len(args.deveui):
self.send_cmd('mac set deveui %s' % args.deveui)
self.send_cmd('mac join otaa')
def join_abp(self):
if len(args.devaddr):
self.send_cmd('mac set devaddr %s' % args.devaddr)
if len(args.appskey):
self.send_cmd('mac set appskey %s' % args.appskey)
if len(args.nwkskey):
self.send_cmd('mac set nwkskey %s' % args.nwkskey)
self.send_cmd('mac join abp')
def connection_made(self, transport):
"""
Fires when connection is made to serial port device
"""
print("Connection to LoStik established")
self.transport = transport
self.retry(self.join)
def handle_line(self, data):
# if data == "ok" or data == 'busy':
# return
print("STATUS: %s" % data)
if data.strip() == "denied" or data.strip() == "no_free_ch":
print("Retrying OTAA connection")
self.retry(self.join)
elif data.strip() == "accepted":
print("UPDATING STATE to connected")
self.state = ConnectionState.CONNECTED
def connection_lost(self, exc):
"""
Called when serial connection is severed to device
"""
if exc:
print(exc)
print("Lost connection to serial device")
def send_cmd(self, cmd, delay=.5):
print(cmd)
self.transport.write(('%s\r\n' % cmd).encode('UTF-8'))
time.sleep(delay)
ser = serial.Serial(args.port, baudrate=57600)
with ReaderThread(ser, PrintLines) as protocol:
while protocol.state < ConnectionState.FAILED:
if protocol.state != ConnectionState.CONNECTED:
time.sleep(1)
continue
protocol.send_cmd("mac tx uncnf 1 %d" % int(time.time()))
time.sleep(10)
exit(protocol.state)

1
rn2903-lorawan.txt Normal file
View File

@@ -0,0 +1 @@
python3 lorawan.py --joinmode otaa --appkey "814BFA6E589EE49376628B0CDD642E1F" --deveui "70B3D57ED80022D3" --appeui 0000000000000000 /dev/tty.usbserial-1140

View File

@@ -81,8 +81,11 @@
let cell4 = mainRow.insertCell();
cell1.innerHTML = key;
cell2.innerHTML = data[key][0][0]; // Frequency (from the first entry)
cell3.innerHTML = data[key][0][1]; // Signal Strength (from the first entry)
// Check for RSSI value and display 'unknown' if it is 0
let rssiValue = data[key][0][1];
cell3.innerHTML = rssiValue === 0 ? 'unknown' : rssiValue; // Signal Strength (from the first entry)
// Create a Bootstrap styled button
let expandBtn = document.createElement('button');
expandBtn.classList.add('btn', 'btn-secondary', 'btn-sm'); // Bootstrap button classes
@@ -90,21 +93,21 @@
expandBtn.onclick = function() {
let deviceKey = key; // Capture the current device key
let isExpanded = !expandedRows[deviceKey]; // Toggle the expanded state
// Show or hide the expandable rows
let nextRow = mainRow.nextSibling;
while (nextRow && nextRow.classList.contains('expandable-row')) {
nextRow.style.display = isExpanded ? 'table-row' : 'none';
nextRow = nextRow.nextSibling;
}
// Update the button text and expandedRows object
expandBtn.innerHTML = isExpanded ? 'Hide Values' : 'Show Values';
expandedRows[deviceKey] = isExpanded;
};
cell4.appendChild(expandBtn);
cell4.style.textAlign = 'center'; // Center align the button
// Add expandable rows for each decoded value
data[key].forEach(entry => {
let expandableRow = tableBody.insertRow();
@@ -114,7 +117,7 @@
cell.colSpan = "4";
cell.innerHTML = `Decoded Value: ${entry[2]}`;
});
if (expandedRows[key]) {
let nextRow = mainRow.nextSibling;
while (nextRow && nextRow.classList.contains('expandable-row')) {
@@ -126,15 +129,15 @@
})
.catch(error => console.error('Error:', error));
}
// Initial update of the table
updateTableData();
// Periodically update the table every 30 seconds (30000 milliseconds)
setInterval(updateTableData, 30000);
// Initial update of the table
updateTableData();
// Periodically update the table every 30 seconds (30000 milliseconds)
setInterval(updateTableData, 30000);
</script>
</section>
<hr class="m-0" />

View File

@@ -79,46 +79,51 @@
let selectedDeviceName = null; // Global variable to store the selected device name
let dataCache = {}; // Cache for storing fetched data
function updateTableData() {
fetch('/get_table_data')
.then(response => response.json())
.then(data => {
const tableBody = document.getElementById('data-table').getElementsByTagName('tbody')[0];
tableBody.innerHTML = ''; // Clear existing table rows
let isRowSelected = selectedDeviceName !== null;
function updateTableData() {
fetch('/get_table_data')
.then(response => response.json())
.then(data => {
const tableBody = document.getElementById('data-table').getElementsByTagName('tbody')[0];
tableBody.innerHTML = ''; // Clear existing table rows
for (let key in data) {
let row = tableBody.insertRow();
row.addEventListener('click', function() {
// Handle row click event for selection
handleRowSelection(row, key);
});
let isRowSelected = selectedDeviceName !== null;
let cell1 = row.insertCell();
let cell2 = row.insertCell();
let cell3 = row.insertCell();
cell1.innerHTML = key; // Device Name
// Assuming that the latest data is the most relevant
let latestData = data[key][data[key].length - 1];
cell2.innerHTML = latestData[0]; // Frequency
cell3.innerHTML = latestData[1]; // Signal Strength
for (let key in data) {
let row = tableBody.insertRow();
row.addEventListener('click', function() {
// Handle row click event for selection
handleRowSelection(row, key);
});
// Apply hiding logic based on selection
if (isRowSelected) {
if (key !== selectedDeviceName) {
row.classList.add('hidden-row');
} else {
row.classList.add('selected-row');
}
let cell1 = row.insertCell();
let cell2 = row.insertCell();
let cell3 = row.insertCell();
cell1.innerHTML = key; // Device Name
// Assuming that the latest data is the most relevant
let latestData = data[key][data[key].length - 1];
cell2.innerHTML = latestData[0]; // Frequency
// Check for RSSI value and display 'unknown' if it is 0
let rssiValue = latestData[1];
cell3.innerHTML = rssiValue === 0 ? 'unknown' : rssiValue; // Signal Strength
// Apply hiding logic based on selection
if (isRowSelected) {
if (key !== selectedDeviceName) {
row.classList.add('hidden-row');
} else {
row.classList.add('selected-row');
}
}
}
updateSelectedDataDisplay(); // Update the selected data display after refreshing the table
})
.catch(error => console.error('Error:', error));
}
updateSelectedDataDisplay(); // Update the selected data display after refreshing the table
})
.catch(error => console.error('Error:', error));
}
function handleRowSelection(row, key) {
// Reset all rows

22
udp-listener.py Normal file
View File

@@ -0,0 +1,22 @@
import socket
# Define the IP address and the port number to listen on.
# '' means the script will listen on all available IPs.
IP = '10.130.1.235'
PORT = 1700
def main():
# Create a UDP socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
# Bind the socket to the address and port
s.bind((IP, PORT))
print(f"Listening for UDP traffic on port {PORT}...")
# Continuously listen for UDP packets
while True:
# Receive data from the client
data, addr = s.recvfrom(1024) # buffer size is 1024 bytes
print(f"Received message from {addr}: {data}")
if __name__ == "__main__":
main()