Compare commits
2 commits
c16badc275
...
2d2fa3836d
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d2fa3836d | |||
| 2d35c6b729 |
3 changed files with 194 additions and 0 deletions
131
src/lib/wifi/demo.ts
Normal file
131
src/lib/wifi/demo.ts
Normal file
|
|
@ -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<boolean> {
|
||||
try {
|
||||
const { value } = await Preferences.get({ key: KEY });
|
||||
aktiv = value === '1';
|
||||
} catch {
|
||||
aktiv = false;
|
||||
}
|
||||
return aktiv;
|
||||
}
|
||||
|
||||
export async function setzeDemoSchalter(an: boolean): Promise<void> {
|
||||
aktiv = an;
|
||||
try {
|
||||
await Preferences.set({ key: KEY, value: an ? '1' : '0' });
|
||||
} catch {
|
||||
/* Schalter gilt dann nur für diese Sitzung */
|
||||
}
|
||||
}
|
||||
|
|
@ -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<UpdateInfo | null>(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
|
||||
</a>
|
||||
|
||||
<!-- WLAN-Demodaten: der Emulator (und ein Gerät ohne Empfang) liefert
|
||||
keine Netze — damit ließe sich die Kanalanalyse gar nicht ansehen.
|
||||
Auch für die Vorführung beim Kunden brauchbar. -->
|
||||
<label class="mt-3 flex items-start gap-2.5">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="mt-0.5 h-4 w-4 shrink-0 accent-fuchsia-500"
|
||||
checked={wifiDemo}
|
||||
onchange={toggleWifiDemo}
|
||||
/>
|
||||
<span class="text-sm">
|
||||
WLAN-Demodaten
|
||||
<span class="mt-0.5 block text-xs text-zinc-500">
|
||||
Die WLAN-Kanalanalyse zeigt dann eine erfundene Beispielumgebung statt echter
|
||||
Messwerte — zum Ansehen und Vorführen ohne Empfang. Gespeicherte Momentaufnahmen
|
||||
werden deutlich als Demo gekennzeichnet.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
import { rssiRating } from '$lib/wifi/rating';
|
||||
import { recommend24GhzChannels, channelWarnings } from '$lib/wifi/recommend';
|
||||
import { netColor } from '$lib/wifi/chart';
|
||||
import { demoNetworks, ladeDemoSchalter, DEMO_CONNECTED_BSSID } from '$lib/wifi/demo';
|
||||
import { sync } from '$lib/sync.svelte';
|
||||
import { toast } from '$lib/toast.svelte';
|
||||
import type { MeasureStatus, Protocol, WifiSurvey } from '$lib/types';
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
let locationError = $state(false);
|
||||
let errorMsg = $state('');
|
||||
let expandedSurvey = $state<string | null>(null);
|
||||
let demoAktiv = $state(false);
|
||||
|
||||
const BANDS = ['2.4 GHz', '5 GHz', '6 GHz'] as const;
|
||||
|
||||
|
|
@ -88,6 +90,7 @@
|
|||
return;
|
||||
}
|
||||
protocol = p;
|
||||
demoAktiv = await ladeDemoSchalter();
|
||||
await refresh();
|
||||
});
|
||||
|
||||
|
|
@ -104,6 +107,16 @@
|
|||
locationError = false;
|
||||
errorMsg = '';
|
||||
try {
|
||||
// Demomodus: der Emulator hat keine WLAN-Hardware und liefert immer 0
|
||||
// Netze — damit ließe sich die gesamte Anzeige nicht prüfen. Der
|
||||
// Schalter steht in den Einstellungen und ist im Kopf der Seite deutlich
|
||||
// sichtbar, damit keine Demomessung versehentlich als echte Messung im
|
||||
// Kundenprotokoll landet.
|
||||
if (demoAktiv) {
|
||||
networks = demoNetworks();
|
||||
connectedBssid = DEMO_CONNECTED_BSSID;
|
||||
return;
|
||||
}
|
||||
const r = await scanner.wifiScan({ trigger: true });
|
||||
networks = r.networks;
|
||||
try {
|
||||
|
|
@ -168,11 +181,16 @@
|
|||
tool: 'wifikanal',
|
||||
category: 'netzwerk',
|
||||
label:
|
||||
(demoAktiv ? 'DEMODATEN — ' : '') +
|
||||
`${survey.networks.length} WLAN-Netze` +
|
||||
(eigenStk ? ` · eigenes Netz Kanal ${eigenStk.channel}, ${eigenStk.rssi} dBm` : '') +
|
||||
(recommendation.length > 0 ? ` · 2,4 GHz frei: Kanal ${recommendation[0].channel}` : ''),
|
||||
params: {},
|
||||
result: {
|
||||
// Muss im Protokoll sichtbar bleiben: eine Demomessung darf nie mit
|
||||
// einer echten Messung verwechselt werden — auch nicht Wochen später
|
||||
// im Kunden-PDF.
|
||||
...(demoAktiv ? { demodaten: true } : {}),
|
||||
anzahlNetze: survey.networks.length,
|
||||
eigenesNetz: eigenStk
|
||||
? {
|
||||
|
|
@ -278,6 +296,14 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
{#if demoAktiv}
|
||||
<p class="mt-2 rounded-lg bg-fuchsia-900/40 p-2.5 text-xs text-fuchsia-200">
|
||||
<strong>Demodaten aktiv</strong> — die angezeigten Netze sind erfunden, nicht gemessen.
|
||||
Eine hier gespeicherte Momentaufnahme wird als Demo gekennzeichnet. Abschalten unter
|
||||
Einstellungen → WLAN-Demodaten.
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#if fazit}
|
||||
<p class="mt-2 rounded-lg bg-zinc-800 p-2.5 text-sm font-medium text-zinc-100">{fazit}</p>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Reference in a new issue