Fix BT Locate startup/map rendering and CelesTrak import reliability

This commit is contained in:
Smittix
2026-02-20 17:35:57 +00:00
parent c0221ba53d
commit c3bf30b49c
6 changed files with 331 additions and 141 deletions

View File

@@ -38,6 +38,7 @@ const BtLocate = (function() {
let lastRenderedDetectionKey = null;
let pendingHeatSync = false;
let mapStabilizeTimer = null;
let modeActive = false;
const MAX_HEAT_POINTS = 1200;
const MAX_TRAIL_POINTS = 1200;
@@ -85,6 +86,7 @@ const BtLocate = (function() {
}
function init() {
modeActive = true;
loadOverlayPreferences();
syncOverlayControls();
@@ -92,7 +94,7 @@ const BtLocate = (function() {
// Re-invalidate map on re-entry and ensure tiles are present
if (map) {
setTimeout(() => {
safeInvalidateMap();
safeInvalidateMap(true);
// Re-apply user's tile layer if tiles were lost
let hasTiles = false;
map.eachLayer(layer => {
@@ -142,7 +144,7 @@ const BtLocate = (function() {
flushPendingHeatSync();
});
setTimeout(() => {
safeInvalidateMap();
safeInvalidateMap(true);
flushPendingHeatSync();
}, 100);
scheduleMapStabilization();
@@ -158,10 +160,10 @@ const BtLocate = (function() {
initialized = true;
}
function checkStatus() {
fetch('/bt_locate/status')
.then(r => r.json())
.then(data => {
function checkStatus() {
fetch('/bt_locate/status')
.then(r => r.json())
.then(data => {
if (data.active) {
sessionStartedAt = data.started_at ? new Date(data.started_at).getTime() : Date.now();
showActiveUI();
@@ -171,12 +173,22 @@ const BtLocate = (function() {
}
})
.catch(() => {});
}
function start() {
const mac = document.getElementById('btLocateMac')?.value.trim();
const namePattern = document.getElementById('btLocateNamePattern')?.value.trim();
const irk = document.getElementById('btLocateIrk')?.value.trim();
}
function normalizeMacInput(value) {
const raw = (value || '').trim().toUpperCase().replace(/-/g, ':');
if (!raw) return '';
const compact = raw.replace(/[^0-9A-F]/g, '');
if (compact.length === 12) {
return compact.match(/.{1,2}/g).join(':');
}
return raw;
}
function start() {
const mac = normalizeMacInput(document.getElementById('btLocateMac')?.value);
const namePattern = document.getElementById('btLocateNamePattern')?.value.trim();
const irk = document.getElementById('btLocateIrk')?.value.trim();
const body = { environment: currentEnvironment };
if (mac) body.mac_address = mac;
@@ -205,13 +217,25 @@ const BtLocate = (function() {
return;
}
fetch('/bt_locate/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
.then(r => r.json())
.then(data => {
fetch('/bt_locate/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
.then(async (r) => {
let data = null;
try {
data = await r.json();
} catch (_) {
data = {};
}
if (!r.ok || data.status !== 'started') {
const message = data.error || data.message || ('HTTP ' + r.status);
throw new Error(message);
}
return data;
})
.then(data => {
if (data.status === 'started') {
sessionStartedAt = data.session?.started_at ? new Date(data.session.started_at).getTime() : Date.now();
showActiveUI();
@@ -224,8 +248,12 @@ const BtLocate = (function() {
restoreTrail();
}
})
.catch(err => console.error('[BtLocate] Start error:', err));
}
.catch(err => {
console.error('[BtLocate] Start error:', err);
alert('BT Locate failed to start: ' + (err?.message || 'Unknown error'));
showIdleUI();
});
}
function stop() {
fetch('/bt_locate/stop', { method: 'POST' })
@@ -888,7 +916,10 @@ const BtLocate = (function() {
if (!map) return;
ensureHeatLayer();
if (!heatLayer) return;
if (!isMapContainerVisible()) {
if (!modeActive || !isMapContainerVisible()) {
if (map.hasLayer(heatLayer)) {
map.removeLayer(heatLayer);
}
pendingHeatSync = true;
return;
}
@@ -918,6 +949,40 @@ const BtLocate = (function() {
}
}
function setActiveMode(active) {
modeActive = !!active;
if (!map) return;
if (!modeActive) {
stopMapStabilization();
if (heatLayer && map.hasLayer(heatLayer)) {
map.removeLayer(heatLayer);
}
pendingHeatSync = true;
return;
}
setTimeout(() => {
if (!modeActive) return;
safeInvalidateMap(true);
if (typeof window.requestAnimationFrame === 'function') {
window.requestAnimationFrame(() => {
if (!modeActive) return;
safeInvalidateMap(true);
window.requestAnimationFrame(() => {
if (!modeActive) return;
safeInvalidateMap(true);
});
});
}
syncHeatLayer();
syncMovementLayer();
syncStrongestMarker();
updateConfidenceLayer();
scheduleMapStabilization(14);
}, 80);
}
function isMapRenderable() {
if (!map || !isMapContainerVisible()) return false;
if (typeof map.getSize === 'function') {
@@ -927,9 +992,26 @@ const BtLocate = (function() {
return true;
}
function safeInvalidateMap() {
function refreshBaseTiles() {
if (!map || typeof L === 'undefined' || typeof map.eachLayer !== 'function') return;
map.eachLayer((layer) => {
if (layer instanceof L.TileLayer && typeof layer.redraw === 'function') {
try {
layer.redraw();
} catch (_) {}
}
});
}
function safeInvalidateMap(forceRecenter = false) {
if (!map || !isMapContainerVisible()) return false;
map.invalidateSize({ pan: false, animate: false });
map.invalidateSize({ pan: !!forceRecenter, animate: false });
if (forceRecenter) {
const center = map.getCenter();
const zoom = map.getZoom();
map.setView(center, zoom, { animate: false });
}
refreshBaseTiles();
return true;
}
@@ -950,7 +1032,7 @@ const BtLocate = (function() {
stopMapStabilization();
return;
}
if (safeInvalidateMap()) {
if (safeInvalidateMap(true)) {
flushPendingHeatSync();
syncMovementLayer();
syncStrongestMarker();
@@ -1624,7 +1706,7 @@ const BtLocate = (function() {
}
function invalidateMap() {
if (safeInvalidateMap()) {
if (safeInvalidateMap(true)) {
flushPendingHeatSync();
syncMovementLayer();
syncStrongestMarker();
@@ -1633,10 +1715,11 @@ const BtLocate = (function() {
scheduleMapStabilization(8);
}
return {
init,
start,
stop,
return {
init,
setActiveMode,
start,
stop,
handoff,
clearHandoff,
setEnvironment,
@@ -1651,4 +1734,6 @@ const BtLocate = (function() {
invalidateMap,
fetchPairedIrks,
};
})();
})();
window.BtLocate = BtLocate;