This commit is contained in:
2025-10-23 02:20:05 +02:00
parent d77fcd8ef5
commit 6c70f9b81f
6 changed files with 287 additions and 248 deletions

View File

@@ -499,6 +499,8 @@ async function shutdownApp() {
try {
await spotCache.init();
soundManager.setEnabled(false);
// ✅ Charger les données du cache immédiatement
const cachedSpots = await spotCache.getSpots();
if (cachedSpots.length > 0) {
@@ -627,6 +629,7 @@ async function shutdownApp() {
{dxccProgress}
{logs}
on:toast={(e) => showToast(e.detail.message, e.detail.type)}
on:clearLogs={() => logs = []}
/>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<script>
import { createEventDispatcher } from 'svelte';
import { createEventDispatcher, onMount } from 'svelte';
export let stats;
export let solarData;
@@ -7,15 +7,20 @@
export let cacheLoaded = false;
export let soundManager;
let soundEnabled = true;
let soundEnabled = false; // ✅ Initialisé à false
const dispatch = createEventDispatcher();
function toggleSound() {
soundEnabled = !soundEnabled;
soundManager.setEnabled(soundEnabled);
}
// ✅ Synchroniser avec le soundManager au montage
onMount(() => {
soundEnabled = soundManager.isEnabled();
});
function toggleSound() {
soundEnabled = !soundEnabled;
soundManager.setEnabled(soundEnabled);
}
function getSFIColor(sfi) {
const value = parseInt(sfi);
if (isNaN(value)) return 'text-slate-500';
@@ -58,89 +63,102 @@
<h1 class="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
FlexDXCluster
</h1>
<div class="flex items-center gap-3 text-xs text-slate-400">
<span>{stats.myCallsign || 'N/A'}<span>{stats.totalContacts}</span> Contacts</span>
<div class="flex items-center gap-2 text-xs text-slate-400">
<!-- Callsign & Contacts -->
<span class="font-semibold text-blue-400">{stats.myCallsign || 'N/A'}</span>
<span class="text-slate-600"></span>
<span>{stats.totalContacts} QSOs</span>
<!-- Spots count -->
<span class="text-slate-600"></span>
<span class="text-pink-400 font-semibold">{stats.totalSpots || 0}</span>
<span>spots</span>
<span class="text-slate-600">|</span>
<span class="flex items-center gap-1">
<span class="font-semibold text-amber-400">SFI:</span>
<!-- Solar data compacts -->
<span class="flex items-center gap-0.5">
<span class="text-amber-400">SFI</span>
<span class={getSFIColor(solarData.sfi)}>{solarData.sfi}</span>
</span>
<span class="flex items-center gap-1">
<span class="font-semibold text-yellow-400">SSN:</span>
<span class="flex items-center gap-0.5">
<span class="text-yellow-400">SSN</span>
<span class={getSunspotsColor(solarData.sunspots)}>{solarData.sunspots}</span>
</span>
<span class="flex items-center gap-1">
<span class="font-semibold text-red-400">A:</span>
<span class="flex items-center gap-0.5">
<span class="text-red-400">A</span>
<span class={getAIndexColor(solarData.aIndex)}>{solarData.aIndex}</span>
</span>
<span class="flex items-center gap-1">
<span class="font-semibold text-purple-400">K:</span>
<span class="flex items-center gap-0.5">
<span class="text-purple-400">K</span>
<span class={getKIndexColor(solarData.kIndex)}>{solarData.kIndex}</span>
</span>
</div>
</div>
</div>
<div class="flex items-center gap-3">
<div class="flex items-center gap-2">
{#if wsStatus === 'connected'}
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold bg-green-500/20 text-green-400">
<span class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></span>
WebSocket
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold bg-green-500/20 text-green-400">
<span class="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse"></span>
WS
</span>
{:else if wsStatus === 'connecting'}
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold bg-orange-500/20 text-orange-400">
<span class="w-2 h-2 bg-orange-500 rounded-full animate-pulse"></span>
Connecting...
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold bg-orange-500/20 text-orange-400">
<span class="w-1.5 h-1.5 bg-orange-500 rounded-full animate-pulse"></span>
WS
</span>
{:else}
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold bg-red-500/20 text-red-400">
<span class="w-2 h-2 bg-red-500 rounded-full"></span>
Disconnected
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold bg-red-500/20 text-red-400">
<span class="w-1.5 h-1.5 bg-red-500 rounded-full"></span>
WS
</span>
{/if}
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold {stats.clusterStatus === 'connected' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
<span class="w-2 h-2 {stats.clusterStatus === 'connected' ? 'bg-green-500 animate-pulse' : 'bg-red-500'} rounded-full"></span>
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold {stats.clusterStatus === 'connected' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
<span class="w-1.5 h-1.5 {stats.clusterStatus === 'connected' ? 'bg-green-500 animate-pulse' : 'bg-red-500'} rounded-full"></span>
Cluster
</span>
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold {stats.flexStatus === 'connected' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
<span class="w-2 h-2 {stats.flexStatus === 'connected' ? 'bg-green-500 animate-pulse' : 'bg-red-500'} rounded-full"></span>
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold {stats.flexStatus === 'connected' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
<span class="w-1.5 h-1.5 {stats.flexStatus === 'connected' ? 'bg-green-500 animate-pulse' : 'bg-red-500'} rounded-full"></span>
Flex
</span>
{#if cacheLoaded}
<span class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-semibold bg-purple-500/20 text-purple-400">
<span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold bg-purple-500/20 text-purple-400" title="Data loaded from cache">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path>
</svg>
Cached
</span>
{/if}
<button
on:click={toggleSound}
title="{soundEnabled ? 'Disable' : 'Enable'} sound alerts"
class="px-3 py-1.5 rounded transition-colors {soundEnabled ? 'bg-blue-600 hover:bg-blue-700' : 'bg-slate-700 hover:bg-slate-600'} flex items-center gap-2">
class="px-2.5 py-1 rounded transition-colors {soundEnabled ? 'bg-blue-600 hover:bg-blue-700' : 'bg-slate-700 hover:bg-slate-600'} flex items-center gap-1.5">
{#if soundEnabled}
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM14.657 2.929a1 1 0 011.414 0A9.972 9.972 0 0119 10a9.972 9.972 0 01-2.929 7.071 1 1 0 01-1.414-1.414A7.971 7.971 0 0017 10c0-2.21-.894-4.208-2.343-5.657a1 1 0 010-1.414zm-2.829 2.828a1 1 0 011.415 0A5.983 5.983 0 0115 10a5.984 5.984 0 01-1.757 4.243 1 1 0 01-1.415-1.415A3.984 3.984 0 0013 10a3.983 3.983 0 00-1.172-2.828 1 1 0 010-1.415z" clip-rule="evenodd"></path>
</svg>
{:else}
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M9.383 3.076A1 1 0 0110 4v12a1 1 0 01-1.707.707L4.586 13H2a1 1 0 01-1-1V8a1 1 0 011-1h2.586l3.707-3.707a1 1 0 011.09-.217zM12.293 7.293a1 1 0 011.414 0L15 8.586l1.293-1.293a1 1 0 111.414 1.414L16.414 10l1.293 1.293a1 1 0 01-1.414 1.414L15 11.414l-1.293 1.293a1 1 0 01-1.414-1.414L13.586 10l-1.293-1.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
{/if}
<span class="text-xs hidden sm:inline">{soundEnabled ? 'Sound ON' : 'Sound OFF'}</span>
<span class="text-xs hidden lg:inline">{soundEnabled ? 'ON' : 'OFF'}</span>
</button>
<button
on:click={() => dispatch('shutdown')}
class="px-3 py-1.5 text-xs bg-red-600 hover:bg-red-700 rounded transition-colors flex items-center gap-1">
class="px-2.5 py-1 text-xs bg-red-600 hover:bg-red-700 rounded transition-colors flex items-center gap-1">
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
Shutdown
<span class="hidden lg:inline">Shutdown</span>
</button>
</div>
</div>

View File

@@ -1,6 +1,10 @@
<script>
import { createEventDispatcher } from 'svelte';
export let logs = [];
const dispatch = createEventDispatcher();
let autoScroll = true;
let container;
let selectedLevels = {
@@ -19,7 +23,7 @@
// ✅ Auto-scroll UNIQUEMENT si activé
$: if (autoScroll && container && filteredLogs.length > 0) {
setTimeout(() => {
if (autoScroll) { // ✅ Vérifier à nouveau car peut avoir changé
if (autoScroll) {
container.scrollTop = container.scrollHeight;
}
}, 10);
@@ -47,8 +51,9 @@
}
}
// ✅ Dispatcher l'événement au parent
function clearLogs() {
logs = [];
dispatch('clearLogs');
}
function toggleLevel(level) {

View File

@@ -12,15 +12,20 @@
export let recentQSOs;
export let logStats;
export let dxccProgress;
export let showOnlyActive = false; // ✅ Export pour persister l'état
export let showOnlyActive = false;
export let logs = [];
const dispatch = createEventDispatcher();
// ✅ Propagation des évènements vers le parent
// ✅ Propagation des événements vers le parent
function handleToast(event) {
dispatch('toast', event.detail);
}
// ✅ Propager clearLogs vers App.svelte
function handleClearLogs() {
dispatch('clearLogs');
}
</script>
<div class="bg-slate-800/50 backdrop-blur rounded-lg border border-slate-700/50 flex flex-col h-full" style="height: 100%; max-height: 100%;">
@@ -83,7 +88,10 @@
{dxccProgress}
/>
{:else if activeTab === 'logs'}
<LogsTab {logs} />
<LogsTab
{logs}
on:clearLogs={handleClearLogs}
/>
{/if}
</div>
</div>

View File

@@ -773,7 +773,6 @@ func (s *HTTPServer) getWatchlistSpotsWithStatus(w http.ResponseWriter, r *http.
for _, pattern := range watchlistCallsigns {
if spot.DX == pattern || strings.HasPrefix(spot.DX, pattern) {
isInWatchlist = true
Log.Debugf("✅ Watchlist match in API: %s matches pattern %s", spot.DX, pattern)
break
}
}
@@ -783,8 +782,6 @@ func (s *HTTPServer) getWatchlistSpotsWithStatus(w http.ResponseWriter, r *http.
}
}
Log.Debugf("📊 Watchlist spots API returned %d spots", len(relevantSpots))
type BandModeKey struct {
Band string
Mode string

View File

@@ -1,74 +1,34 @@
[
{
"callsign": "PY0FB",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:24.3843986+02:00",
"spotCount": 0,
"callsign": "FW5K",
"lastSeen": "2025-10-22T22:37:24.0393589+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:17:37.9061157+02:00",
"spotCount": 140,
"playSound": true
},
{
"callsign": "D2A",
"lastSeen": "2025-10-23T02:16:36.4176115+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-20T22:11:35.4767205+02:00",
"spotCount": 180,
"playSound": true
},
{
"callsign": "5H3MB",
"lastSeen": "2025-10-20T22:27:00.0920217+02:00",
"lastSeenStr": "20 hours ago",
"lastSeen": "2025-10-22T21:21:06.1309604+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:18:42.8402097+02:00",
"spotCount": 6,
"spotCount": 20,
"playSound": true
},
{
"callsign": "C5R",
"lastSeen": "2025-10-21T19:27:01.1572273+02:00",
"lastSeen": "2025-10-23T02:17:36.6578871+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:18:04.5006892+02:00",
"spotCount": 219,
"playSound": true
},
{
"callsign": "SU0ERA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:45.8848244+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "XT2AW",
"lastSeen": "2025-10-21T04:49:21.3973928+02:00",
"lastSeenStr": "14 hours ago",
"addedAt": "2025-10-18T17:17:27.3839089+02:00",
"spotCount": 69,
"playSound": true
},
{
"callsign": "3B8M",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:32.6851135+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "5K0UA",
"lastSeen": "2025-10-21T19:26:43.7522993+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:17:53.7390559+02:00",
"spotCount": 437,
"playSound": true
},
{
"callsign": "9L8MD",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:56.7896868+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "E6AD",
"lastSeen": "2025-10-20T20:08:38.1641735+02:00",
"lastSeenStr": "23 hours ago",
"addedAt": "2025-10-18T17:17:40.8765179+02:00",
"spotCount": 459,
"spotCount": 307,
"playSound": true
},
{
@@ -80,26 +40,26 @@
"playSound": true
},
{
"callsign": "4X6TT",
"lastSeen": "2025-10-19T19:59:28.3446792+02:00",
"lastSeenStr": "1 day ago",
"addedAt": "2025-10-18T17:18:13.335878+02:00",
"spotCount": 1,
"playSound": true
},
{
"callsign": "PJ6Y",
"lastSeen": "2025-10-21T19:25:29.1180452+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:17:47.7237081+02:00",
"spotCount": 664,
"playSound": true
},
{
"callsign": "YI1MB",
"callsign": "9L8MD",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:18.825584+02:00",
"addedAt": "2025-10-18T17:18:56.7896868+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "YJ0CA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:33.3921665+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "SU0ERA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:45.8848244+02:00",
"spotCount": 0,
"playSound": true
},
@@ -112,10 +72,26 @@
"playSound": true
},
{
"callsign": "VP2M",
"callsign": "DP0GVN",
"lastSeen": "2025-10-23T02:03:51.5639848+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-20T07:00:51.7088369+02:00",
"spotCount": 86,
"playSound": true
},
{
"callsign": "5J0EA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:57.308717+02:00",
"addedAt": "2025-10-18T17:17:51.0758741+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "5X2I",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:14.6598633+02:00",
"spotCount": 0,
"playSound": true
},
@@ -128,122 +104,10 @@
"playSound": true
},
{
"callsign": "5R8IC",
"lastSeen": "2025-10-21T07:25:10.6322383+02:00",
"lastSeenStr": "11 hours ago",
"addedAt": "2025-10-19T18:18:58.8382325+02:00",
"spotCount": 61,
"playSound": true
},
{
"callsign": "XV9",
"callsign": "PY0FB",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:24.9155327+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "5J0EA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:51.0758741+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "VP8LP",
"lastSeen": "2025-10-20T22:46:40.9079579+02:00",
"lastSeenStr": "20 hours ago",
"addedAt": "2025-10-18T17:18:49.0576187+02:00",
"spotCount": 21,
"playSound": true
},
{
"callsign": "EL2BG",
"lastSeen": "2025-10-21T05:41:57.870261+02:00",
"lastSeenStr": "13 hours ago",
"addedAt": "2025-10-18T17:18:10.2000017+02:00",
"spotCount": 22,
"playSound": true
},
{
"callsign": "YJ0CA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:33.3921665+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "TZ4AM",
"lastSeen": "2025-10-21T01:27:17.0524499+02:00",
"lastSeenStr": "17 hours ago",
"addedAt": "2025-10-18T17:19:00.3154177+02:00",
"spotCount": 46,
"playSound": true
},
{
"callsign": "H44MS",
"lastSeen": "2025-10-20T19:29:23.5019952+02:00",
"lastSeenStr": "23 hours ago",
"addedAt": "2025-10-18T17:16:49.1572859+02:00",
"spotCount": 1,
"playSound": true
},
{
"callsign": "TJ1GD",
"lastSeen": "2025-10-21T07:18:19.1978587+02:00",
"lastSeenStr": "12 hours ago",
"addedAt": "2025-10-18T17:18:27.6004027+02:00",
"spotCount": 41,
"playSound": true
},
{
"callsign": "C5LT",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:07.2442738+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "C8K",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:39.8627992+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "FW5K",
"lastSeen": "2025-10-21T07:17:42.220076+02:00",
"lastSeenStr": "12 hours ago",
"addedAt": "2025-10-18T17:17:37.9061157+02:00",
"spotCount": 111,
"playSound": true
},
{
"callsign": "Z66IPA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:36.5251607+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "V85NPV",
"lastSeen": "2025-10-19T15:42:31.3912491+02:00",
"lastSeenStr": "2 days ago",
"addedAt": "2025-10-18T17:18:15.8781583+02:00",
"spotCount": 2,
"playSound": true
},
{
"callsign": "5X2I",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:14.6598633+02:00",
"addedAt": "2025-10-18T17:17:24.3843986+02:00",
"spotCount": 0,
"playSound": true
},
@@ -256,19 +120,163 @@
"playSound": true
},
{
"callsign": "DP0GVN",
"lastSeen": "2025-10-21T07:20:49.6910744+02:00",
"lastSeenStr": "12 hours ago",
"addedAt": "2025-10-20T07:00:51.7088369+02:00",
"spotCount": 77,
"callsign": "3B8M",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:32.6851135+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "D2A",
"lastSeen": "2025-10-21T07:21:58.7128427+02:00",
"lastSeenStr": "11 hours ago",
"addedAt": "2025-10-20T22:11:35.4767205+02:00",
"spotCount": 76,
"callsign": "PJ6Y",
"lastSeen": "2025-10-23T02:17:52.6776808+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:17:47.7237081+02:00",
"spotCount": 864,
"playSound": true
},
{
"callsign": "YI1MB",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:18.825584+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "TJ1GD",
"lastSeen": "2025-10-23T00:29:20.5443895+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:18:27.6004027+02:00",
"spotCount": 62,
"playSound": true
},
{
"callsign": "EL2BG",
"lastSeen": "2025-10-22T19:26:22.9261316+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:18:10.2000017+02:00",
"spotCount": 24,
"playSound": true
},
{
"callsign": "6O3T",
"lastSeen": "2025-10-23T02:14:10.5255505+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-22T19:31:13.1154881+02:00",
"spotCount": 138,
"playSound": true
},
{
"callsign": "4X6TT",
"lastSeen": "2025-10-21T19:54:01.9678474+02:00",
"lastSeenStr": "23 hours ago",
"addedAt": "2025-10-18T17:18:13.335878+02:00",
"spotCount": 4,
"playSound": true
},
{
"callsign": "XT2AW",
"lastSeen": "2025-10-23T00:20:38.5329853+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:17:27.3839089+02:00",
"spotCount": 92,
"playSound": true
},
{
"callsign": "V85NPV",
"lastSeen": "2025-10-19T15:42:31.3912491+02:00",
"lastSeenStr": "3 days ago",
"addedAt": "2025-10-18T17:18:15.8781583+02:00",
"spotCount": 2,
"playSound": true
},
{
"callsign": "VP8LP",
"lastSeen": "2025-10-20T22:46:40.9079579+02:00",
"lastSeenStr": "1 day ago",
"addedAt": "2025-10-18T17:18:49.0576187+02:00",
"spotCount": 21,
"playSound": true
},
{
"callsign": "C5LT",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:07.2442738+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "Z66IPA",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:36.5251607+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "TZ4AM",
"lastSeen": "2025-10-23T00:08:32.5194313+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:19:00.3154177+02:00",
"spotCount": 54,
"playSound": true
},
{
"callsign": "E6AD",
"lastSeen": "2025-10-20T20:08:38.1641735+02:00",
"lastSeenStr": "1 day ago",
"addedAt": "2025-10-18T17:17:40.8765179+02:00",
"spotCount": 459,
"playSound": true
},
{
"callsign": "H44MS",
"lastSeen": "2025-10-20T19:29:23.5019952+02:00",
"lastSeenStr": "1 day ago",
"addedAt": "2025-10-18T17:16:49.1572859+02:00",
"spotCount": 1,
"playSound": true
},
{
"callsign": "C8K",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:39.8627992+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "5K0UA",
"lastSeen": "2025-10-23T02:07:24.3949315+02:00",
"lastSeenStr": "Just now",
"addedAt": "2025-10-18T17:17:53.7390559+02:00",
"spotCount": 637,
"playSound": true
},
{
"callsign": "XV9",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:18:24.9155327+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "VP2M",
"lastSeen": "0001-01-01T00:00:00Z",
"lastSeenStr": "Never",
"addedAt": "2025-10-18T17:17:57.308717+02:00",
"spotCount": 0,
"playSound": true
},
{
"callsign": "5R8IC",
"lastSeen": "2025-10-21T07:25:10.6322383+02:00",
"lastSeenStr": "1 day ago",
"addedAt": "2025-10-19T18:18:58.8382325+02:00",
"spotCount": 61,
"playSound": true
}
]