mirror of
https://github.com/skinnyrad/Lora-Scanner.git
synced 2026-03-28 17:43:00 +01:00
54
app.py
54
app.py
@@ -6,6 +6,7 @@ import threading
|
||||
import time
|
||||
from collections import deque
|
||||
import pandas as pd
|
||||
import re
|
||||
|
||||
app = Flask(__name__)
|
||||
socketio = SocketIO(app)
|
||||
@@ -19,25 +20,44 @@ 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 = {}
|
||||
|
||||
|
||||
def read_serial_data(port, ser, buffer):
|
||||
global global_dataframe
|
||||
global surveydata
|
||||
rssi_pattern = r"RSSI: (-?\d+)"
|
||||
rssi = ''
|
||||
|
||||
while True:
|
||||
try:
|
||||
if ser.in_waiting > 0:
|
||||
data = ser.readline().decode('utf-8').strip()
|
||||
match = re.search(rssi_pattern, data)
|
||||
|
||||
# Check if a RSSI was found
|
||||
if match:
|
||||
if port =='port1':
|
||||
rssi = int(match.group(1))
|
||||
surveydata['Raw LoRa Device 443 MHz'] = [433,rssi,'']
|
||||
elif port =='port2':
|
||||
rssi = int(match.group(1))
|
||||
surveydata['Raw LoRa Device 868 MHz'] = [868,rssi,'']
|
||||
elif port =='port3':
|
||||
rssi = int(match.group(1))
|
||||
surveydata['Raw LoRa Device 915 MHz'] = [915,rssi,'']
|
||||
|
||||
buffer.append(data)
|
||||
socketio.emit(f'serial_data_{port}', {'data': data})
|
||||
|
||||
|
||||
if (global_dataframe['Device Name'] == 'Unknown Raw LoRa Devices').any():
|
||||
pass
|
||||
else:
|
||||
pandasdata = {'Device Name': 'Unknown Raw LoRa Devices', 'Frequency': frequency(port)}
|
||||
global_dataframe = global_dataframe.append(pandasdata, ignore_index=True)
|
||||
'''print(global_dataframe.head())'''
|
||||
|
||||
socketio.emit(f'serial_data_{port}', {'data': data})
|
||||
if frequency(port) == 433 and surveydata.get('Raw LoRa Device 443 MHz') is None:
|
||||
surveydata['Raw LoRa Device 443 MHz'] = [433,0,rssi]
|
||||
|
||||
elif frequency(port) == 868 and surveydata.get('Raw LoRa Device 868 MHz') is None:
|
||||
surveydata['Raw LoRa Device 868 MHz'] = [868,0,rssi]
|
||||
|
||||
elif frequency(port) == 915 and surveydata.get('Raw LoRa Device 915 MHz') is None:
|
||||
surveydata['Raw LoRa Device 915 MHz'] = [915,0,rssi]
|
||||
|
||||
|
||||
if (port == 'port1' and port1_status == False):
|
||||
return
|
||||
if (port == 'port2' and port2_status == False):
|
||||
@@ -47,7 +67,7 @@ def read_serial_data(port, ser, buffer):
|
||||
time.sleep(0.1)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def connect_serial(port,frequency):
|
||||
global ser1
|
||||
@@ -114,7 +134,7 @@ def analysis():
|
||||
|
||||
@app.route('/survey')
|
||||
def survey():
|
||||
return render_template('survey.html', initial_data={port: list(buffer) for port, buffer in serial_buffers.items()})
|
||||
return render_template('survey.html', data=global_dataframe)
|
||||
|
||||
@app.route('/tracking')
|
||||
def tracking():
|
||||
@@ -221,8 +241,12 @@ def checkSer():
|
||||
except:
|
||||
pass
|
||||
return jsonify(result="False")
|
||||
|
||||
|
||||
|
||||
@app.route('/get_table_data')
|
||||
def get_table_data():
|
||||
global surveydata
|
||||
print(surveydata)
|
||||
return jsonify(surveydata)
|
||||
|
||||
if __name__ == '__main__':
|
||||
socketio.run(app, debug=True)
|
||||
|
||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Flask==2.3.2
|
||||
Flask_SocketIO==5.3.6
|
||||
MarkupSafe==2.1.3
|
||||
pandas==2.0.3
|
||||
pyserial==3.5
|
||||
@@ -11062,4 +11062,38 @@ section.resume-section .resume-section-content {
|
||||
|
||||
.red {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
/* Styling for the table */
|
||||
.scrollable-table {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid black;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.selected-row {
|
||||
background-color: #FFFF99; /* Highlight color */
|
||||
font-weight: bold; /* Enlarge the font */
|
||||
}
|
||||
|
||||
.hidden-row {
|
||||
display: none; /* Hide the non-selected rows */
|
||||
}
|
||||
|
||||
.center-button {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -52,25 +52,18 @@
|
||||
<p>
|
||||
Analyze LoRa traffic received at 433, 868, or 915 MHz. Click the desired frequency below to get started. Once you are on the appropriate page, click the 'Connect Serial Port' button to connect to a serial port on your computer. Once connected to your LoRa receiver, traffic will automatically be streamed to the web page for analysis. To disconnect a receiver, click the 'Disconnect Serial Port' button.
|
||||
</p>
|
||||
<h2 class="lead mb-5">
|
||||
<a class="nav-link js-scroll-trigger" href="#433">433 MHz</a>
|
||||
<br>
|
||||
<a class="nav-link js-scroll-trigger" href="#868">868 MHz</a>
|
||||
<br>
|
||||
<a class="nav-link js-scroll-trigger" href="#915">915 MHz</a>
|
||||
</h2>
|
||||
</div>
|
||||
</section>
|
||||
<hr class="m-0" />
|
||||
<hr class="m-0" />
|
||||
<br><br>
|
||||
<!-- 433 -->
|
||||
<section class="resume-section" id="433">
|
||||
<div class="resume-section-content">
|
||||
<h2 class="mb-5">433 MHz</h2>
|
||||
<!-- Collapsible content -->
|
||||
<div class="collapse" id="collapse433">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
|
||||
<div class="flex-grow-1">
|
||||
<h3 class="mb-0">LoRa Transceiver</h3><br>
|
||||
<h3 class="mb-0">433 MHz LoRa Transceiver</h3><br>
|
||||
<div class="serial-data" id="serial-data-port1">
|
||||
</div>
|
||||
|
||||
<div style="text-align:center;">
|
||||
<button class="styled-button" onclick="promptUser433()">Connect Serial Port</button>
|
||||
<button class="disconnect-button" onclick="deleteSerial433()">Disconnect Serial Port</button>
|
||||
@@ -235,18 +228,20 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<hr class="m-0" />
|
||||
<!-- 868 -->
|
||||
<section class="resume-section" id="868">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Collapsible button -->
|
||||
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse433" role="button" aria-expanded="false" aria-controls="collapse433">
|
||||
Toggle 433 MHz Section
|
||||
</a>
|
||||
</div>
|
||||
<br><br>
|
||||
<div class="resume-section-content">
|
||||
<h2 class="mb-5">868 MHz</h2>
|
||||
<div class="collapse" id="collapse868">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
|
||||
<div class="flex-grow-1">
|
||||
<h3 class="mb-0">LoRa Transceiver</h3><br>
|
||||
<h3 class="mb-0">868 MHz LoRa Transceiver</h3><br>
|
||||
<div class="serial-data" id="serial-data-port2">
|
||||
</div>
|
||||
<div style="text-align:center;">
|
||||
@@ -296,22 +291,17 @@
|
||||
<script type="text/javascript">
|
||||
var socket = io.connect('http://' + document.domain + ':' + location.port);
|
||||
|
||||
// For port1
|
||||
socket.on('initial_serial_data_port1', function(data) {
|
||||
var serialDataDiv = document.getElementById('serial-data-port1');
|
||||
socket.on('serial_data_port1', function(data) {
|
||||
var serialDataDiv = document.getElementById('serial-data-port1');
|
||||
if (data.data instanceof Array) {
|
||||
data.data.forEach(function(line) {
|
||||
serialDataDiv.innerHTML += line + '<br>';
|
||||
});
|
||||
// Scroll to the bottom for new data
|
||||
//serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
|
||||
});
|
||||
|
||||
socket.on('serial_data_port1', function(data) {
|
||||
var serialDataDiv = document.getElementById('serial-data-port1');
|
||||
serialDataDiv.innerHTML += data.data + '<br>';
|
||||
// Scroll to the bottom for new data
|
||||
//serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
|
||||
});
|
||||
}
|
||||
// Scroll to the bottom for new data
|
||||
//serialDataDiv.scrollTop = serialDataDiv.scrollHeight;
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<script>
|
||||
@@ -433,15 +423,20 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</section>
|
||||
<hr class="m-0" />
|
||||
<!-- 915-->
|
||||
<section class="resume-section" id="915">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Collapsible button -->
|
||||
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse868" role="button" aria-expanded="false" aria-controls="collapse868">
|
||||
Toggle 868 MHz Section
|
||||
</a>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<div class="resume-section-content">
|
||||
<h2 class="mb-5">915 MHz</h2><br>
|
||||
<div class="collapse" id="collapse915">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
|
||||
<div class="flex-grow-1">
|
||||
<h3 class="mb-0">LoRa Transceiver</h3><br>
|
||||
<h3 class="mb-0">915 MHZ LoRa Transceiver</h3><br>
|
||||
<div class="serial-data" id="serial-data-port3">
|
||||
</div>
|
||||
<div style="text-align:center;">
|
||||
@@ -606,8 +601,17 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Collapsible button -->
|
||||
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse915" role="button" aria-expanded="false" aria-controls="collapse915">
|
||||
Toggle 915 MHz Section
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- Bootstrap core JS-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/analysis">Analysis Mode</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/survey">Survey Mode</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#">Tracking Mode</a></li>
|
||||
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="/tracking">Tracking Mode</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<!-- Include SweetAlert2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body id="page-top">
|
||||
@@ -45,52 +45,55 @@
|
||||
<!-- About-->
|
||||
<section class="resume-section" id="about">
|
||||
<div class="resume-section-content">
|
||||
<h1 class="mb-0">
|
||||
<h1 class="mb-0" style="text-align: center;">
|
||||
Survey Mode
|
||||
</h1>
|
||||
<h2 class="lead mb-5">Skinny Research and Development</h2>
|
||||
</div>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>DataFrame Table</title>
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #dddddd;
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
</style>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Device Name</th>
|
||||
<th>Frequency</th>
|
||||
<th>Signal Strength</th>
|
||||
<th>Plaintext</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for index, row in data.iterrows() %}
|
||||
<tr>
|
||||
<td>{{ row['Device Name'] }}</td>
|
||||
<td>{{ row['Frequency'] }}</td>
|
||||
<td>{{ row['Signal Strength'] }}</td>
|
||||
<td>{{ row['Plaintext'] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<br>
|
||||
<div class="scrollable-table">
|
||||
<table id="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Device Name</th>
|
||||
<th>Frequency</th>
|
||||
<th>Signal Strength</th>
|
||||
<th>Recovered Plaintext</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Rows will be inserted here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
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();
|
||||
let cell1 = row.insertCell();
|
||||
let cell2 = row.insertCell();
|
||||
let cell3 = row.insertCell();
|
||||
let cell4 = row.insertCell();
|
||||
cell1.innerHTML = key;
|
||||
cell2.innerHTML = data[key][0];
|
||||
cell3.innerHTML = data[key][1];
|
||||
cell4.innerHTML = data[key][2];
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
// Initial update of the table
|
||||
updateTableData();
|
||||
|
||||
// Periodically update the table every 30 seconds (30000 milliseconds)
|
||||
setInterval(updateTableData, 30000);
|
||||
</script>
|
||||
|
||||
</section>
|
||||
<hr class="m-0" />
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
|
||||
<!-- Include SweetAlert2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||
</head>
|
||||
<body id="page-top">
|
||||
<!-- Navigation-->
|
||||
@@ -45,11 +44,141 @@
|
||||
<!-- About-->
|
||||
<section class="resume-section" id="about">
|
||||
<div class="resume-section-content">
|
||||
<h1 class="mb-0">
|
||||
<h1 class="mb-0" style="text-align: center;">
|
||||
Tracking Mode
|
||||
</h1>
|
||||
<h2 class="lead mb-5">Skinny Research and Development</h2>
|
||||
</div>
|
||||
<br>
|
||||
<h2 class="lead mb-5" style="text-align: center;">Select A Device You Would Like To Track</h2>
|
||||
<br>
|
||||
<div class="scrollable-table">
|
||||
<table id="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Device Name</th>
|
||||
<th>Frequency</th>
|
||||
<th>Signal Strength</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Rows will be inserted here -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<!-- Add a Clear Selection button with a CSS class -->
|
||||
<button id="clear-selection-button" class="center-button" style="display: none;">Clear Filter</button>
|
||||
|
||||
<style>
|
||||
/* CSS styles for centering the button */
|
||||
.center-button {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* CSS styles to hide rows */
|
||||
.hidden-row {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
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 => {
|
||||
dataCache = data; // Update the data cache
|
||||
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 () {
|
||||
// Hide all rows
|
||||
const allRows = tableBody.getElementsByTagName('tr');
|
||||
for (let i = 0; i < allRows.length; i++) {
|
||||
allRows[i].classList.add('hidden-row');
|
||||
}
|
||||
|
||||
// Show the clicked row
|
||||
row.classList.remove('hidden-row');
|
||||
row.classList.add('selected-row');
|
||||
|
||||
// Display the "Clear Filter" button
|
||||
const clearButton = document.getElementById('clear-selection-button');
|
||||
clearButton.style.display = 'block';
|
||||
|
||||
selectedDeviceName = key; // Update the global variable with the selected device name
|
||||
updateSelectedDataDisplay(); // Update the selected data display
|
||||
});
|
||||
|
||||
if (Object.keys(data).indexOf(key) > 0) {
|
||||
row.classList.add('hidden-row');
|
||||
}
|
||||
|
||||
let cell1 = row.insertCell();
|
||||
let cell2 = row.insertCell();
|
||||
let cell3 = row.insertCell();
|
||||
cell1.innerHTML = key;
|
||||
cell2.innerHTML = data[key][0];
|
||||
cell3.innerHTML = data[key][1];
|
||||
}
|
||||
|
||||
updateSelectedDataDisplay(); // Update the selected data display after refreshing the table
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
|
||||
function updateSelectedDataDisplay() {
|
||||
const selectedDataDiv = document.getElementById('selected-data');
|
||||
if (selectedDeviceName !== null && selectedDeviceName in dataCache) {
|
||||
const frequency = dataCache[selectedDeviceName][0];
|
||||
const signalStrength = dataCache[selectedDeviceName][1];
|
||||
selectedDataDiv.innerHTML = `
|
||||
Device Name: ${selectedDeviceName}<br>
|
||||
Frequency: ${frequency}<br>
|
||||
Signal Strength: ${signalStrength}<br>
|
||||
`;
|
||||
} else {
|
||||
selectedDataDiv.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
const clearButton = document.getElementById('clear-selection-button');
|
||||
clearButton.addEventListener('click', function () {
|
||||
const tableBody = document.getElementById('data-table').getElementsByTagName('tbody')[0];
|
||||
const selectedRow = tableBody.getElementsByClassName('selected-row')[0];
|
||||
if (selectedRow) {
|
||||
selectedRow.classList.remove('selected-row');
|
||||
}
|
||||
|
||||
// Show all rows
|
||||
const allRows = tableBody.getElementsByTagName('tr');
|
||||
for (let i = 0; i < allRows.length; i++) {
|
||||
allRows[i].classList.remove('hidden-row');
|
||||
}
|
||||
|
||||
// Hide the "Clear Filter" button
|
||||
clearButton.style.display = 'none';
|
||||
|
||||
selectedDeviceName = null; // Reset the global variable when selection is cleared
|
||||
updateSelectedDataDisplay(); // Clear the selected data display
|
||||
});
|
||||
|
||||
// Initial update of the table
|
||||
updateTableData();
|
||||
|
||||
// Periodically update the table every second
|
||||
setInterval(updateTableData, 1000);
|
||||
</script>
|
||||
|
||||
|
||||
<!-- Add a section to display selected data -->
|
||||
<div id="selected-data"></div>
|
||||
|
||||
</section>
|
||||
<hr class="m-0" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user