From 8608ee627222c172f3cbc1a46c198da023930efa Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Tue, 28 Jul 2026 14:18:34 +0200 Subject: [PATCH] feat(agent): netsh WiFi fallback labels for all enrollment-geography languages Derived the language set from actual enrollment geography (get_geographic_metrics, 365d, 10k sessions across ~50 countries) and documented the country->language mapping on the key arrays. Newly covered: Finnish, Hungarian, Lithuanian, Latvian, Romanian, Croatian/ Bosnian, Slovak, Indonesian/Malay, Vietnamese, Thai, Arabic, Bulgarian radio-type variant and Swedish "Radiotyp". New fi/id parser tests; the unknown-language tests now use Greek (outside the geography set). Co-Authored-By: Claude Fable 5 --- .../Interop/NetshWifiFallbackTests.cs | 46 ++++++- .../Monitoring/Interop/NetshWifiFallback.cs | 127 ++++++++++++------ 2 files changed, 125 insertions(+), 48 deletions(-) diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Interop/NetshWifiFallbackTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Interop/NetshWifiFallbackTests.cs index ab693040..ac4cc833 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Interop/NetshWifiFallbackTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Interop/NetshWifiFallbackTests.cs @@ -139,11 +139,49 @@ Es ist 1 Schnittstelle auf dem System vorhanden: Assert.Equal("contoso-corp", r!.Ssid); } + [Fact] + public void Parse_finnish_output_extracts_all_fields() + { + const string output = @" + Nimi : Wi-Fi + SSID : contoso-fi + Kanava : 100 + Radiotyyppi : 802.11ax + Signaali : 79% +"; + var r = NetshWifiFallback.Parse(output); + + Assert.NotNull(r); + Assert.Equal("contoso-fi", r!.Ssid); + Assert.Equal(79, r.SignalPercent); + Assert.Equal("802.11ax", r.RadioType); + Assert.Equal(100, r.Channel); + } + + [Fact] + public void Parse_indonesian_output_extracts_all_fields() + { + const string output = @" + Nama : Wi-Fi + SSID : contoso-id + Saluran : 11 + Jenis radio : 802.11n + Sinyal : 64% +"; + var r = NetshWifiFallback.Parse(output); + + Assert.NotNull(r); + Assert.Equal("contoso-id", r!.Ssid); + Assert.Equal(64, r.SignalPercent); + Assert.Equal("802.11n", r.RadioType); + Assert.Equal(11, r.Channel); + } + [Fact] public void Parse_unknown_language_without_ssid_returns_null() { - // No recognizable labels at all -> nothing to report. - var r = NetshWifiFallback.Parse(" Yhteystila : yhdistetty\r\n Taajuus : 5 GHz\r\n"); + // Greek is not in the enrollment-geography label set -> nothing to report. + var r = NetshWifiFallback.Parse(" Κατάσταση : συνδεδεμένο\r\n Σήμα : 90%\r\n"); Assert.Null(r); } @@ -152,10 +190,10 @@ Es ist 1 Schnittstelle auf dem System vorhanden: public void Parse_unknown_language_still_yields_ssid() { // SSID is untranslated in all locales — partial result beats none. - var r = NetshWifiFallback.Parse(" SSID : contoso-fi\r\n Signaali : 90%\r\n"); + var r = NetshWifiFallback.Parse(" SSID : contoso-gr\r\n Σήμα : 90%\r\n"); Assert.NotNull(r); - Assert.Equal("contoso-fi", r!.Ssid); + Assert.Equal("contoso-gr", r!.Ssid); Assert.Null(r.SignalPercent); } diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Interop/NetshWifiFallback.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Interop/NetshWifiFallback.cs index d599bc3f..b18c1be3 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Interop/NetshWifiFallback.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Interop/NetshWifiFallback.cs @@ -12,10 +12,10 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Interop /// root-caused (suspect: Wi-Fi location gating, 24H2+), netsh stays as the safety net. /// /// netsh labels follow the OS UI language, so the parser matches the label variants of - /// the languages commonly seen in enrollments (EN/DE/FR/ES/PT/IT/NL/PL/CS/TR/RU/JA/ - /// ZH-CN/ZH-TW/KO + the Scandinavian ones that reuse EN/DE spellings). Unknown-language - /// labels fall through harmlessly — the affected field is simply absent, same as before. - /// "SSID" itself is untranslated in all known locales. + /// every language implied by the enrollment geography seen to date (see the comment on + /// the key arrays for the country→language derivation). Unknown-language labels fall + /// through harmlessly — the affected field is simply absent, same as before. "SSID" + /// itself is untranslated in all known locales. /// /// internal static class NetshWifiFallback @@ -24,56 +24,95 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Interop // Label variants per field, by OS UI language. Matched case-insensitively against the // full text left of the colon, so acronym-suffixed keys ("AP BSSID") never collide. + // Language set derived from enrollment geography (get_geographic_metrics, 365d): + // US/GB/AU/CA/IE/SG/IN/ZA/NG/PH en · DE/AT/CH de · BE/NL nl · BE/FR/CH fr · DK da · + // SE sv · PL pl · TR tr · CN zh-CN · HK zh-TW · ES/MX/PE/PY es · CZ cs · SK sk · + // IT/CH it · ID id · MY ms · FI fi · LT lt · LV lv · HU hu · BR/PT pt · JP ja · + // KR ko · TH th · VN vi · RO/MD ro · BG bg · BA/HR hr · AE/SA/EG/JO ar · + // MN/GE/AZ usually en/ru images → ru. private static readonly string[] SignalKeys = { - "Signal", // en, de, fr, sv, da, nb - "Señal", // es - "Sinal", // pt - "Segnale", // it - "Signaal", // nl - "Sygnał", // pl - "Signál", // cs - "Sinyal", // tr - "Сигнал", // ru - "シグナル", // ja - "信号", // zh-CN - "訊號", // zh-TW - "신호", // ko + "Signal", // en, de, fr, sv, da, nb, hr + "Señal", // es + "Sinal", // pt + "Segnale", // it + "Signaal", // nl + "Sygnał", // pl + "Signál", // cs, sk + "Sinyal", // tr, id + "Isyarat", // ms + "Signaali", // fi + "Signalas", // lt + "Signāls", // lv + "Jel", // hu + "Semnal", // ro + "Tín hiệu", // vi + "สัญญาณ", // th + "الإشارة", // ar + "إشارة", // ar (variant without article) + "Сигнал", // ru, bg + "シグナル", // ja + "信号", // zh-CN + "訊號", // zh-TW + "신호", // ko }; private static readonly string[] RadioTypeKeys = { - "Radio type", // en - "Funktyp", // de - "Type de radio", // fr - "Tipo de radio", // es - "Tipo de rádio", // pt - "Tipo di radio", // it - "Radiotype", // nl - "Typ radia", // pl - "Radyo türü", // tr - "Тип радио", // ru - "無線の種類", // ja - "无线电类型", // zh-CN - "無線電類型", // zh-TW - "라디오 종류", // ko - "무선 종류", // ko (variant) + "Radio type", // en + "Funktyp", // de + "Type de radio", // fr + "Tipo de radio", // es + "Tipo de rádio", // pt + "Tipo di radio", // it + "Radiotype", // nl, da + "Radiotyp", // sv + "Typ radia", // pl + "Typ rádia", // cs, sk + "Radyo türü", // tr + "Jenis radio", // id, ms + "Radiotyyppi", // fi + "Radijo tipas", // lt + "Radio tips", // lv + "Rádió típusa", // hu + "Tip radio", // ro + "Vrsta radija", // hr, bs + "Loại radio", // vi + "ชนิดวิทยุ", // th + "نوع الراديو", // ar + "Тип радио", // ru + "Тип на радиото", // bg + "無線の種類", // ja + "无线电类型", // zh-CN + "無線電類型", // zh-TW + "라디오 종류", // ko + "무선 종류", // ko (variant) }; private static readonly string[] ChannelKeys = { - "Channel", // en - "Kanal", // de, sv, da, nb, tr - "Canal", // fr, es, pt - "Canale", // it - "Kanaal", // nl - "Kanał", // pl - "Kanál", // cs - "Канал", // ru - "チャネル", // ja - "信道", // zh-CN - "通道", // zh-TW / zh-CN (variant) - "채널", // ko + "Channel", // en + "Kanal", // de, sv, da, nb, tr, hr + "Canal", // fr, es, pt, ro + "Canale", // it + "Kanaal", // nl + "Kanał", // pl + "Kanál", // cs, sk + "Saluran", // id, ms + "Kanava", // fi + "Kanalas", // lt + "Kanāls", // lv + "Csatorna", // hu + "Kênh", // vi + "ช่องสัญญาณ", // th + "ช่อง", // th (variant) + "القناة", // ar + "قناة", // ar (variant without article) + "Канал", // ru, bg + "チャネル", // ja + "信道", // zh-CN + "通道", // zh-TW / zh-CN (variant) + "채널", // ko }; ///