From 8497469d2e97a4f60c14c20ef0579375a80ccf84 Mon Sep 17 00:00:00 2001
From: Colonel Panic <90460753+colonelpanichacks@users.noreply.github.com>
Date: Thu, 28 Aug 2025 20:21:54 -0400
Subject: [PATCH] Add files via upload
---
api/flockyou.py | 8 +++
api/requirements.txt | 4 +-
api/templates/index.html | 140 +++++++++++++++++++++++++++++++++++----
3 files changed, 138 insertions(+), 14 deletions(-)
diff --git a/api/flockyou.py b/api/flockyou.py
index c7d36ac..ea3d49e 100644
--- a/api/flockyou.py
+++ b/api/flockyou.py
@@ -138,10 +138,18 @@ def gps_reader():
try:
line = serial_connection.readline().decode('utf-8', errors='ignore')
if line:
+ # Send raw GPS data to serial terminal
+ safe_socket_emit('serial_data', f"GPS: {line.strip()}", room='serial_terminal')
+
parsed = parse_nmea_sentence(line)
if parsed:
gps_data = parsed
safe_socket_emit('gps_update', parsed)
+
+ # Also send parsed GPS data to terminal
+ if parsed.get('fix_quality') > 0:
+ gps_info = f"GPS Fix: {parsed.get('latitude', 'N/A')}, {parsed.get('longitude', 'N/A')} - {parsed.get('satellites', 0)} satellites"
+ safe_socket_emit('serial_data', gps_info, room='serial_terminal')
except Exception as e:
print(f"GPS read error: {e}")
with connection_lock:
diff --git a/api/requirements.txt b/api/requirements.txt
index 2ea9410..1d8ff29 100644
--- a/api/requirements.txt
+++ b/api/requirements.txt
@@ -1,7 +1,7 @@
-Flask==2.3.3
+Flask==2.2.5
Flask-SocketIO==5.3.6
python-socketio==5.8.0
python-engineio==4.7.1
pyserial==3.5
-Werkzeug==2.3.7
+Werkzeug==2.2.3
requests==2.31.0
diff --git a/api/templates/index.html b/api/templates/index.html
index b33447c..7a20994 100644
--- a/api/templates/index.html
+++ b/api/templates/index.html
@@ -86,6 +86,37 @@
gap: 0.5rem;
}
+ .port-controls {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ }
+
+ .refresh-btn {
+ background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%);
+ color: white;
+ font-weight: 600;
+ border: 1px solid #4b5563;
+ padding: 0.3rem 0.5rem;
+ font-size: 0.8rem;
+ min-width: auto;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .refresh-btn:hover {
+ background: linear-gradient(135deg, #9ca3af 0%, #d1d5db 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(107, 114, 128, 0.4);
+ }
+
+ .refresh-btn:active {
+ transform: translateY(0);
+ }
+
.header-buttons {
display: flex;
align-items: center;
@@ -605,6 +636,11 @@
100% { opacity: 1; }
}
+ @keyframes spin {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+ }
+
.main-content {
padding: 1rem;
width: 100%;
@@ -914,9 +950,12 @@
-
+
+
+
+
@@ -924,9 +963,12 @@
-
+
+
+
+
@@ -1036,6 +1078,7 @@
let detections = [];
let gpsConnected = false;
const max_reconnect_attempts = 5;
+ let userInteractingWithPorts = false; // Flag to prevent auto-refresh interference
// Initialize
document.addEventListener('DOMContentLoaded', function() {
@@ -1049,11 +1092,14 @@
// Periodic status refresh every 5 seconds
setInterval(loadStatus, 5000);
- // Periodic port refresh every 10 seconds
+ // Periodic port refresh every 30 seconds (reduced from 10 seconds)
setInterval(function() {
- loadFlockPorts();
- loadGpsPorts();
- }, 10000);
+ // Only refresh if user is not actively interacting with ports
+ if (!userInteractingWithPorts) {
+ loadFlockPorts();
+ loadGpsPorts();
+ }
+ }, 30000);
// Connection health check every 30 seconds
setInterval(function() {
@@ -1073,6 +1119,22 @@
document.getElementById('disconnectFlockBtn').addEventListener('click', disconnectFlock);
document.getElementById('connectGpsBtn').addEventListener('click', connectGps);
document.getElementById('disconnectGpsBtn').addEventListener('click', disconnectGps);
+
+ // Add event listeners for port dropdowns to prevent auto-refresh interference
+ const flockSelect = document.getElementById('flockDeviceSelect');
+ const gpsSelect = document.getElementById('gpsPortSelect');
+
+ flockSelect.addEventListener('focus', () => { userInteractingWithPorts = true; });
+ flockSelect.addEventListener('blur', () => {
+ setTimeout(() => { userInteractingWithPorts = false; }, 1000);
+ });
+ flockSelect.addEventListener('change', () => { userInteractingWithPorts = true; });
+
+ gpsSelect.addEventListener('focus', () => { userInteractingWithPorts = true; });
+ gpsSelect.addEventListener('blur', () => {
+ setTimeout(() => { userInteractingWithPorts = false; }, 1000);
+ });
+ gpsSelect.addEventListener('change', () => { userInteractingWithPorts = true; });
}
function loadDetections() {
@@ -1090,11 +1152,24 @@
}
function loadGpsPorts() {
+ const select = document.getElementById('gpsPortSelect');
+ const refreshBtn = document.getElementById('refreshGpsPortsBtn');
+ const currentSelection = select.value; // Preserve current selection
+
+ // Show loading state
+ refreshBtn.textContent = '⟳';
+ refreshBtn.style.animation = 'spin 1s linear infinite';
+
+ // Add a subtle visual indicator if this is an auto-refresh
+ if (!userInteractingWithPorts) {
+ refreshBtn.title = 'Auto-refreshing ports...';
+ }
+
fetch('/api/gps/ports')
.then(response => response.json())
.then(ports => {
console.log('GPS ports loaded:', ports.length);
- const select = document.getElementById('gpsPortSelect');
+
select.innerHTML = '';
ports.forEach(port => {
const option = document.createElement('option');
@@ -1102,18 +1177,45 @@
option.textContent = `${port.device} - ${port.description}`;
select.appendChild(option);
});
+
+ // Restore selection if it still exists in the new list
+ if (currentSelection && ports.some(p => p.device === currentSelection)) {
+ select.value = currentSelection;
+ }
+
+ // Reset refresh button
+ refreshBtn.textContent = '↻';
+ refreshBtn.style.animation = '';
+ refreshBtn.title = 'Refresh ports';
})
.catch(error => {
console.error('Error loading GPS ports:', error);
+ // Reset refresh button on error
+ refreshBtn.textContent = '↻';
+ refreshBtn.style.animation = '';
+ refreshBtn.title = 'Refresh ports';
});
}
function loadFlockPorts() {
+ const select = document.getElementById('flockDeviceSelect');
+ const refreshBtn = document.getElementById('refreshFlockPortsBtn');
+ const currentSelection = select.value; // Preserve current selection
+
+ // Show loading state
+ refreshBtn.textContent = '⟳';
+ refreshBtn.style.animation = 'spin 1s linear infinite';
+
+ // Add a subtle visual indicator if this is an auto-refresh
+ if (!userInteractingWithPorts) {
+ refreshBtn.title = 'Auto-refreshing ports...';
+ }
+
fetch('/api/flock/ports')
.then(response => response.json())
.then(ports => {
console.log('Flock ports loaded:', ports.length);
- const select = document.getElementById('flockDeviceSelect');
+
select.innerHTML = '';
ports.forEach(port => {
const option = document.createElement('option');
@@ -1121,9 +1223,23 @@
option.textContent = `${port.device} - ${port.description}`;
select.appendChild(option);
});
+
+ // Restore selection if it still exists in the new list
+ if (currentSelection && ports.some(p => p.device === currentSelection)) {
+ select.value = currentSelection;
+ }
+
+ // Reset refresh button
+ refreshBtn.textContent = '↻';
+ refreshBtn.style.animation = '';
+ refreshBtn.title = 'Refresh ports';
})
.catch(error => {
console.error('Error loading Flock ports:', error);
+ // Reset refresh button on error
+ refreshBtn.textContent = '↻';
+ refreshBtn.style.animation = '';
+ refreshBtn.title = 'Refresh ports';
});
}