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 { sync } from '$lib/sync.svelte';
|
||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import { APP_VERSION, checkForUpdate, installUpdate, type UpdateInfo } from '$lib/updater';
|
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 checking = $state(false);
|
||||||
let available = $state<UpdateInfo | null>(null);
|
let available = $state<UpdateInfo | null>(null);
|
||||||
let installing = $state(false);
|
let installing = $state(false);
|
||||||
let percent = $state(0);
|
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
|
// Sync-Status in Klartext
|
||||||
const syncLabel = $derived(
|
const syncLabel = $derived(
|
||||||
|
|
@ -220,6 +237,26 @@
|
||||||
>
|
>
|
||||||
Debug-Log öffnen
|
Debug-Log öffnen
|
||||||
</a>
|
</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>
|
</section>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
import { rssiRating } from '$lib/wifi/rating';
|
import { rssiRating } from '$lib/wifi/rating';
|
||||||
import { recommend24GhzChannels, channelWarnings } from '$lib/wifi/recommend';
|
import { recommend24GhzChannels, channelWarnings } from '$lib/wifi/recommend';
|
||||||
import { netColor } from '$lib/wifi/chart';
|
import { netColor } from '$lib/wifi/chart';
|
||||||
|
import { demoNetworks, ladeDemoSchalter, DEMO_CONNECTED_BSSID } from '$lib/wifi/demo';
|
||||||
import { sync } from '$lib/sync.svelte';
|
import { sync } from '$lib/sync.svelte';
|
||||||
import { toast } from '$lib/toast.svelte';
|
import { toast } from '$lib/toast.svelte';
|
||||||
import type { MeasureStatus, Protocol, WifiSurvey } from '$lib/types';
|
import type { MeasureStatus, Protocol, WifiSurvey } from '$lib/types';
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
let locationError = $state(false);
|
let locationError = $state(false);
|
||||||
let errorMsg = $state('');
|
let errorMsg = $state('');
|
||||||
let expandedSurvey = $state<string | null>(null);
|
let expandedSurvey = $state<string | null>(null);
|
||||||
|
let demoAktiv = $state(false);
|
||||||
|
|
||||||
const BANDS = ['2.4 GHz', '5 GHz', '6 GHz'] as const;
|
const BANDS = ['2.4 GHz', '5 GHz', '6 GHz'] as const;
|
||||||
|
|
||||||
|
|
@ -88,6 +90,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
protocol = p;
|
protocol = p;
|
||||||
|
demoAktiv = await ladeDemoSchalter();
|
||||||
await refresh();
|
await refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -104,6 +107,16 @@
|
||||||
locationError = false;
|
locationError = false;
|
||||||
errorMsg = '';
|
errorMsg = '';
|
||||||
try {
|
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 });
|
const r = await scanner.wifiScan({ trigger: true });
|
||||||
networks = r.networks;
|
networks = r.networks;
|
||||||
try {
|
try {
|
||||||
|
|
@ -168,11 +181,16 @@
|
||||||
tool: 'wifikanal',
|
tool: 'wifikanal',
|
||||||
category: 'netzwerk',
|
category: 'netzwerk',
|
||||||
label:
|
label:
|
||||||
|
(demoAktiv ? 'DEMODATEN — ' : '') +
|
||||||
`${survey.networks.length} WLAN-Netze` +
|
`${survey.networks.length} WLAN-Netze` +
|
||||||
(eigenStk ? ` · eigenes Netz Kanal ${eigenStk.channel}, ${eigenStk.rssi} dBm` : '') +
|
(eigenStk ? ` · eigenes Netz Kanal ${eigenStk.channel}, ${eigenStk.rssi} dBm` : '') +
|
||||||
(recommendation.length > 0 ? ` · 2,4 GHz frei: Kanal ${recommendation[0].channel}` : ''),
|
(recommendation.length > 0 ? ` · 2,4 GHz frei: Kanal ${recommendation[0].channel}` : ''),
|
||||||
params: {},
|
params: {},
|
||||||
result: {
|
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,
|
anzahlNetze: survey.networks.length,
|
||||||
eigenesNetz: eigenStk
|
eigenesNetz: eigenStk
|
||||||
? {
|
? {
|
||||||
|
|
@ -278,6 +296,14 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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}
|
{#if fazit}
|
||||||
<p class="mt-2 rounded-lg bg-zinc-800 p-2.5 text-sm font-medium text-zinc-100">{fazit}</p>
|
<p class="mt-2 rounded-lg bg-zinc-800 p-2.5 text-sm font-medium text-zinc-100">{fazit}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue