diff --git a/src/lib/wifi/demo.ts b/src/lib/wifi/demo.ts new file mode 100644 index 0000000..1e9d10d --- /dev/null +++ b/src/lib/wifi/demo.ts @@ -0,0 +1,131 @@ +/** + * WLAN-Demodaten für Vorführung und Prüfung ohne echten Empfang. + * + * Warum das nötig ist: der Android-Emulator hat keine WLAN-Hardware und + * liefert bei jedem Scan null Netze. Kanalgraph, Gruppierung, Kanalempfehlung + * und die Warnregeln ließen sich damit auf dem Gerät überhaupt nicht ansehen — + * die erste Fassung der Diagramme ging deshalb unbemerkt unlesbar in den + * Einsatz. Mit diesem Schalter zeigt die App eine realistische Kundensituation + * und die gesamte Anzeigekette lässt sich vor Ort wie im Emulator prüfen. + * + * Zweiter Nutzen: Vorführung beim Kunden ohne Abhängigkeit davon, was gerade + * in der Luft liegt. + * + * Die Daten sind bewusst so gewählt, dass sie die Grenzfälle abdecken: + * - Mesh (dieselbe SSID auf mehreren BSSIDs und Bändern) + * - mehrere Netze auf demselben Kanal (Co-Channel) + * - ein 40-MHz-Netz im 2,4-GHz-Band (löst Warnregel 1 aus) + * - ein DFS-Kanal und ein 160-MHz-Netz im 5-GHz-Band (löst Warnregel 2 aus) + * - Netze in allen drei UNII-Blöcken, damit die 5-GHz-Aufteilung sichtbar wird + * - ein Netz am Bandrand und ein offenes Netz ohne Standard-Angabe + */ + +import { Preferences } from '@capacitor/preferences'; +import type { WifiNetwork } from '../scanner'; + +const KEY = 'netdiag_wifi_demo'; + +/** BSSID des Netzes, das im Demomodus als „verbunden" gilt */ +export const DEMO_CONNECTED_BSSID = 'AA:BB:CC:10:20:01'; + +export const DEMO_NETWORKS: WifiNetwork[] = [ + // Eigenes Netz (Mesh über 2,4 + 5 GHz) + { + ssid: 'FRITZ!Box 7590 SM', bssid: DEMO_CONNECTED_BSSID, channel: 6, frequency: 2437, + rssi: -48, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 800, + }, + { + ssid: 'FRITZ!Box 7590 SM', bssid: 'AA:BB:CC:10:20:02', channel: 44, frequency: 5220, + rssi: -55, band: '5 GHz', widthMhz: 80, centerFreq0: 5210, + standard: 'Wi-Fi 6 (802.11ax)', security: 'WPA3', ageMs: 900, + }, + // Gastnetz auf demselben Kanal wie das Hauptnetz (Co-Channel) + { + ssid: 'Sandmann-Gast', bssid: 'AA:BB:CC:10:20:03', channel: 6, frequency: 2437, + rssi: -52, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 1100, + }, + // Repeater (Mesh, zweiter Standort) — 5 GHz auf DFS-Kanal (Warnregel) + { + ssid: 'Repeater-Halle', bssid: 'AA:BB:CC:10:20:04', channel: 11, frequency: 2462, + rssi: -61, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 4 (802.11n)', security: 'WPA2', ageMs: 2200, + }, + { + ssid: 'Repeater-Halle', bssid: 'AA:BB:CC:10:20:05', channel: 100, frequency: 5500, + rssi: -70, band: '5 GHz', widthMhz: 80, centerFreq0: 5530, + standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 2600, + }, + // Nachbar mit 40 MHz im 2,4-GHz-Band (Warnregel) + { + ssid: 'Nachbar-WLAN-1', bssid: 'DD:EE:FF:30:40:01', channel: 1, frequency: 2412, + rssi: -74, band: '2.4 GHz', widthMhz: 40, centerFreq0: 2422, + standard: 'Wi-Fi 4 (802.11n)', security: 'WPA2', ageMs: 3400, + }, + { + ssid: 'Telekom-2AB3F', bssid: 'DD:EE:FF:30:40:02', channel: 11, frequency: 2462, + rssi: -79, band: '2.4 GHz', widthMhz: 20, standard: 'Wi-Fi 4 (802.11n)', security: 'WPA2', ageMs: 4100, + }, + // Offenes Netz am oberen Bandrand, ohne Standard-Angabe + { + ssid: 'Vodafone-Hotspot', bssid: 'DD:EE:FF:30:40:03', channel: 13, frequency: 2472, + rssi: -85, band: '2.4 GHz', widthMhz: 20, security: 'offen', ageMs: 5200, + }, + // UNII-1: schwacher Nachbar + Drucker-Direktverbindung + { + ssid: 'o2-WLAN-9912', bssid: 'DD:EE:FF:30:40:04', channel: 36, frequency: 5180, + rssi: -76, band: '5 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 3900, + }, + { + ssid: 'DIRECT-hp-OfficeJet', bssid: 'DD:EE:FF:30:40:05', channel: 48, frequency: 5240, + rssi: -81, band: '5 GHz', widthMhz: 20, standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 4400, + }, + // UNII-3: Kamera + zweiter Büro-AP mit 160 MHz (Warnregel) + { + ssid: 'Kamera-Hof-5G', bssid: 'DD:EE:FF:30:40:06', channel: 149, frequency: 5745, + rssi: -72, band: '5 GHz', widthMhz: 40, centerFreq0: 5755, + standard: 'Wi-Fi 5 (802.11ac)', security: 'WPA2', ageMs: 2900, + }, + { + ssid: 'Buero-AP-2', bssid: 'DD:EE:FF:30:40:07', channel: 157, frequency: 5785, + rssi: -68, band: '5 GHz', widthMhz: 160, centerFreq0: 5775, + standard: 'Wi-Fi 6 (802.11ax)', security: 'WPA2', ageMs: 1800, + }, +]; + +/** + * Demodaten mit leichtem Rauschen — so verändert sich das Bild zwischen zwei + * Scans wie in der Wirklichkeit, und mehrere Momentaufnahmen ergeben einen + * Zeitverlauf mit sichtbarem Auf und Ab statt völlig gerader Linien. + */ +export function demoNetworks(): WifiNetwork[] { + return DEMO_NETWORKS.map((n) => ({ + ...n, + rssi: Math.max(-92, Math.min(-30, n.rssi + Math.round((Math.random() - 0.5) * 6))), + })); +} + +/* --- Schalter (überlebt den App-Neustart) --- */ + +let aktiv = false; + +export function isDemoAktiv(): boolean { + return aktiv; +} + +export async function ladeDemoSchalter(): Promise { + try { + const { value } = await Preferences.get({ key: KEY }); + aktiv = value === '1'; + } catch { + aktiv = false; + } + return aktiv; +} + +export async function setzeDemoSchalter(an: boolean): Promise { + aktiv = an; + try { + await Preferences.set({ key: KEY, value: an ? '1' : '0' }); + } catch { + /* Schalter gilt dann nur für diese Sitzung */ + } +} diff --git a/src/routes/einstellungen/+page.svelte b/src/routes/einstellungen/+page.svelte index 065b04e..f03eb2a 100644 --- a/src/routes/einstellungen/+page.svelte +++ b/src/routes/einstellungen/+page.svelte @@ -7,11 +7,28 @@ import { sync } from '$lib/sync.svelte'; import { toast } from '$lib/toast.svelte'; import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater'; + import { ladeDemoSchalter, setzeDemoSchalter } from '$lib/wifi/demo'; + import { onMount } from 'svelte'; let checking = $state(false); let available = $state(null); let installing = $state(false); let percent = $state(0); + let wifiDemo = $state(false); + + onMount(async () => { + wifiDemo = await ladeDemoSchalter(); + }); + + async function toggleWifiDemo(e: Event) { + const an = (e.currentTarget as HTMLInputElement).checked; + wifiDemo = an; + await setzeDemoSchalter(an); + toast.show( + an ? 'WLAN-Demodaten an — Kanalanalyse zeigt Beispieldaten' : 'WLAN-Demodaten aus', + an ? 'info' : 'success', + ); + } // Sync-Status in Klartext const syncLabel = $derived( @@ -220,6 +237,26 @@ > Debug-Log öffnen + + + + {#if demoAktiv} +

+ Demodaten aktiv — die angezeigten Netze sind erfunden, nicht gemessen. + Eine hier gespeicherte Momentaufnahme wird als Demo gekennzeichnet. Abschalten unter + Einstellungen → WLAN-Demodaten. +

+ {/if} + {#if fazit}

{fazit}

{/if}