mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Merge upstream/main and resolve weather-satellite.js conflict
Resolved conflict in static/js/modes/weather-satellite.js: - Kept allPasses state variable and applyPassFilter() for satellite pass filtering - Kept satellite select dropdown listener for filter feature - Adopted upstream's optimistic stop() UI pattern for better responsiveness - Kept optional chaining (pass?.trajectory) since drawPolarPlot can receive null Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -429,6 +429,7 @@
|
||||
let agentPollTimer = null; // Polling fallback for agent mode
|
||||
let isTracking = false;
|
||||
let currentFilter = 'all';
|
||||
// ICAO -> { emergency: bool, watchlist: bool, military: bool }
|
||||
let alertedAircraft = {};
|
||||
let alertsEnabled = true;
|
||||
let detectionSoundEnabled = localStorage.getItem('adsb_detectionSound') !== 'false'; // Default on
|
||||
@@ -678,24 +679,64 @@
|
||||
}
|
||||
}
|
||||
|
||||
function speakAircraftAlert(kind, icao, ac, detail) {
|
||||
if (typeof VoiceAlerts === 'undefined' || typeof VoiceAlerts.speak !== 'function') return;
|
||||
|
||||
const cfg = (typeof VoiceAlerts.getConfig === 'function')
|
||||
? VoiceAlerts.getConfig()
|
||||
: { streams: {} };
|
||||
const streams = cfg && cfg.streams ? cfg.streams : {};
|
||||
const callsign = (ac && ac.callsign ? String(ac.callsign).trim() : '') || icao;
|
||||
|
||||
if (kind === 'emergency') {
|
||||
if (streams.squawks === false) return;
|
||||
const squawk = detail && detail.squawk ? ` squawk ${detail.squawk}.` : '.';
|
||||
const meaning = detail && detail.name ? ` ${detail.name}.` : '';
|
||||
VoiceAlerts.speak(`Aircraft emergency: ${callsign}.${squawk}${meaning}`, VoiceAlerts.PRIORITY.HIGH);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kind === 'military') {
|
||||
if (streams.adsb_military === false) return;
|
||||
const country = detail && detail.country ? ` ${detail.country}.` : '';
|
||||
VoiceAlerts.speak(`Military aircraft detected: ${callsign}.${country}`, VoiceAlerts.PRIORITY.HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
function checkAndAlertAircraft(icao, ac) {
|
||||
if (alertedAircraft[icao]) return;
|
||||
if (!alertedAircraft[icao]) {
|
||||
alertedAircraft[icao] = { emergency: false, watchlist: false, military: false };
|
||||
}
|
||||
|
||||
const alertState = alertedAircraft[icao];
|
||||
const militaryInfo = isMilitaryAircraft(icao, ac.callsign);
|
||||
const squawkInfo = checkSquawkCode(ac);
|
||||
const onWatchlist = isOnWatchlist(ac);
|
||||
|
||||
if (squawkInfo && squawkInfo.type === 'emergency') {
|
||||
alertedAircraft[icao] = 'emergency';
|
||||
playAlertSound('emergency');
|
||||
showAlertBanner(`EMERGENCY: ${squawkInfo.name} - ${ac.callsign || icao}`, '#ff0000');
|
||||
} else if (onWatchlist) {
|
||||
alertedAircraft[icao] = 'watchlist';
|
||||
if (!alertState.emergency) {
|
||||
alertState.emergency = true;
|
||||
playAlertSound('emergency');
|
||||
showAlertBanner(`EMERGENCY: ${squawkInfo.name} - ${ac.callsign || icao}`, '#ff0000');
|
||||
speakAircraftAlert('emergency', icao, ac, {
|
||||
squawk: ac.squawk,
|
||||
name: squawkInfo.name,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (onWatchlist && !alertState.watchlist) {
|
||||
alertState.watchlist = true;
|
||||
playAlertSound('military'); // Use military sound for watchlist
|
||||
showAlertBanner(`WATCHLIST: ${ac.callsign || ac.registration || icao} detected!`, '#00d4ff');
|
||||
} else if (militaryInfo.military) {
|
||||
alertedAircraft[icao] = 'military';
|
||||
} else if (militaryInfo.military && !alertState.military) {
|
||||
alertState.military = true;
|
||||
playAlertSound('military');
|
||||
showAlertBanner(`MILITARY: ${ac.callsign || icao}${militaryInfo.country ? ' (' + militaryInfo.country + ')' : ''}`, '#556b2f');
|
||||
speakAircraftAlert('military', icao, ac, {
|
||||
country: militaryInfo.country || null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2183,21 +2224,44 @@ sudo make install</code>
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
// Route stop through agent proxy if using remote agent
|
||||
const url = useAgent
|
||||
? `/controller/agents/${adsbCurrentAgent}/adsb/stop`
|
||||
// Route stop through the source that actually started tracking.
|
||||
const stopSource = adsbTrackingSource || (useAgent ? adsbCurrentAgent : 'local');
|
||||
const stopViaAgent = stopSource !== null && stopSource !== undefined && stopSource !== 'local';
|
||||
const url = stopViaAgent
|
||||
? `/controller/agents/${stopSource}/adsb/stop`
|
||||
: '/adsb/stop';
|
||||
await fetch(url, {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({})
|
||||
body: JSON.stringify({ source: 'adsb_dashboard' })
|
||||
});
|
||||
const text = await response.text();
|
||||
let data = {};
|
||||
if (text) {
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid response: ${text}`);
|
||||
}
|
||||
}
|
||||
const result = stopViaAgent && data.result ? data.result : data;
|
||||
const stopped = response.ok && (
|
||||
result.status === 'stopped' ||
|
||||
result.status === 'success' ||
|
||||
data.status === 'success'
|
||||
);
|
||||
if (!stopped) {
|
||||
throw new Error(result.message || data.message || `HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
// Update agent running modes tracking
|
||||
if (useAgent && typeof agentRunningModes !== 'undefined') {
|
||||
if (stopViaAgent && typeof agentRunningModes !== 'undefined') {
|
||||
agentRunningModes = agentRunningModes.filter(m => m !== 'adsb');
|
||||
}
|
||||
} catch (err) {}
|
||||
} catch (err) {
|
||||
alert('Failed to stop ADS-B: ' + err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
stopEventStream();
|
||||
isTracking = false;
|
||||
@@ -2350,16 +2414,17 @@ sudo make install</code>
|
||||
function startEventStream() {
|
||||
if (eventSource) eventSource.close();
|
||||
|
||||
const useAgent = typeof adsbCurrentAgent !== 'undefined' && adsbCurrentAgent !== 'local';
|
||||
const activeSource = (isTracking && adsbTrackingSource) ? adsbTrackingSource : adsbCurrentAgent;
|
||||
const useAgent = typeof activeSource !== 'undefined' && activeSource !== null && activeSource !== 'local';
|
||||
const streamUrl = useAgent ? '/controller/stream/all' : '/adsb/stream';
|
||||
|
||||
console.log(`[ADS-B] startEventStream called - adsbCurrentAgent=${adsbCurrentAgent}, useAgent=${useAgent}, streamUrl=${streamUrl}`);
|
||||
console.log(`[ADS-B] startEventStream called - activeSource=${activeSource}, useAgent=${useAgent}, streamUrl=${streamUrl}`);
|
||||
eventSource = new EventSource(streamUrl);
|
||||
|
||||
// Get agent name for filtering multi-agent stream
|
||||
let targetAgentName = null;
|
||||
if (useAgent && typeof agents !== 'undefined') {
|
||||
const agent = agents.find(a => a.id == adsbCurrentAgent);
|
||||
const agent = agents.find(a => a.id == activeSource);
|
||||
targetAgentName = agent ? agent.name : null;
|
||||
}
|
||||
|
||||
@@ -4253,30 +4318,56 @@ sudo make install</code>
|
||||
.catch(err => alert('VDL2 Error: ' + err));
|
||||
}
|
||||
|
||||
function stopVdl2() {
|
||||
const isAgentMode = vdl2CurrentAgent !== null;
|
||||
async function stopVdl2() {
|
||||
const sourceAgentId = vdl2CurrentAgent;
|
||||
const isAgentMode = sourceAgentId !== null && sourceAgentId !== undefined;
|
||||
const endpoint = isAgentMode
|
||||
? `/controller/agents/${vdl2CurrentAgent}/vdl2/stop`
|
||||
? `/controller/agents/${sourceAgentId}/vdl2/stop`
|
||||
: '/vdl2/stop';
|
||||
|
||||
fetch(endpoint, { method: 'POST' })
|
||||
.then(r => r.json())
|
||||
.then(() => {
|
||||
isVdl2Running = false;
|
||||
vdl2CurrentAgent = null;
|
||||
document.getElementById('vdl2ToggleBtn').innerHTML = '▶ START VDL2';
|
||||
document.getElementById('vdl2ToggleBtn').classList.remove('active');
|
||||
document.getElementById('vdl2PanelIndicator').classList.remove('active');
|
||||
if (vdl2EventSource) {
|
||||
vdl2EventSource.close();
|
||||
vdl2EventSource = null;
|
||||
}
|
||||
// Clear polling timer
|
||||
if (vdl2PollTimer) {
|
||||
clearInterval(vdl2PollTimer);
|
||||
vdl2PollTimer = null;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ source: 'adsb_dashboard' })
|
||||
});
|
||||
const text = await response.text();
|
||||
let data = {};
|
||||
if (text) {
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch (e) {
|
||||
throw new Error(`Invalid response: ${text}`);
|
||||
}
|
||||
}
|
||||
|
||||
const result = isAgentMode && data.result ? data.result : data;
|
||||
const stopped = response.ok && (
|
||||
result.status === 'stopped' ||
|
||||
result.status === 'success' ||
|
||||
data.status === 'success'
|
||||
);
|
||||
if (!stopped) {
|
||||
throw new Error(result.message || data.message || `HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
isVdl2Running = false;
|
||||
vdl2CurrentAgent = null;
|
||||
document.getElementById('vdl2ToggleBtn').innerHTML = '▶ START VDL2';
|
||||
document.getElementById('vdl2ToggleBtn').classList.remove('active');
|
||||
document.getElementById('vdl2PanelIndicator').classList.remove('active');
|
||||
if (vdl2EventSource) {
|
||||
vdl2EventSource.close();
|
||||
vdl2EventSource = null;
|
||||
}
|
||||
// Clear polling timer
|
||||
if (vdl2PollTimer) {
|
||||
clearInterval(vdl2PollTimer);
|
||||
vdl2PollTimer = null;
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Failed to stop VDL2: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync VDL2 UI state (called by syncModeUI in agents.js)
|
||||
@@ -4294,6 +4385,7 @@ sudo make install</code>
|
||||
startVdl2Stream(agentId !== null);
|
||||
}
|
||||
} else {
|
||||
vdl2CurrentAgent = null;
|
||||
btn.innerHTML = '▶ START VDL2';
|
||||
btn.classList.remove('active');
|
||||
if (indicator) indicator.classList.remove('active');
|
||||
@@ -5340,7 +5432,13 @@ sudo make install</code>
|
||||
<!-- Help Modal -->
|
||||
{% include 'partials/help-modal.html' %}
|
||||
|
||||
<script src="{{ url_for('static', filename='js/core/voice-alerts.js') }}?v={{ version }}&r=adsbvoice1"></script>
|
||||
<script src="{{ url_for('static', filename='js/core/settings-manager.js') }}?v={{ version }}&r=maptheme17"></script>
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
if (typeof VoiceAlerts !== 'undefined') VoiceAlerts.init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Agent Manager -->
|
||||
<script src="{{ url_for('static', filename='js/core/agents.js') }}"></script>
|
||||
@@ -5472,24 +5570,27 @@ sudo make install</code>
|
||||
|
||||
if (running) {
|
||||
isTracking = true;
|
||||
const normalizedSource = source === null || source === undefined
|
||||
? (adsbTrackingSource || 'local')
|
||||
: source;
|
||||
|
||||
// If source is an agent ID (not 'local' and not null), update adsbCurrentAgent
|
||||
// This ensures startEventStream uses the correct routing
|
||||
if (source && source !== 'local') {
|
||||
adsbCurrentAgent = source;
|
||||
if (normalizedSource !== 'local') {
|
||||
adsbCurrentAgent = normalizedSource;
|
||||
// Also update the dropdown to match
|
||||
const agentSelect = document.getElementById('agentSelect');
|
||||
if (agentSelect) {
|
||||
agentSelect.value = source;
|
||||
agentSelect.value = normalizedSource;
|
||||
}
|
||||
// Update global agent state too
|
||||
if (typeof currentAgent !== 'undefined') {
|
||||
currentAgent = source;
|
||||
currentAgent = normalizedSource;
|
||||
}
|
||||
console.log(`[ADS-B] Updated adsbCurrentAgent to ${source}`);
|
||||
console.log(`[ADS-B] Updated adsbCurrentAgent to ${normalizedSource}`);
|
||||
}
|
||||
|
||||
adsbTrackingSource = source || adsbCurrentAgent; // Track which source is active
|
||||
adsbTrackingSource = normalizedSource; // Track which source is active
|
||||
|
||||
// Update button
|
||||
if (btn) {
|
||||
@@ -5509,9 +5610,9 @@ sudo make install</code>
|
||||
if (agentSelect) agentSelect.disabled = true;
|
||||
|
||||
// Start data stream for the active source
|
||||
const isAgentSource = adsbCurrentAgent !== 'local';
|
||||
const isAgentSource = normalizedSource !== 'local';
|
||||
if (isAgentSource) {
|
||||
console.log(`[ADS-B] Starting data stream from agent ${adsbCurrentAgent}`);
|
||||
console.log(`[ADS-B] Starting data stream from agent ${normalizedSource}`);
|
||||
startEventStream();
|
||||
drawRangeRings();
|
||||
startSessionTimer();
|
||||
|
||||
Reference in New Issue
Block a user