From b9c51fd010efe2db9722999363a6337f5f2d1a14 Mon Sep 17 00:00:00 2001 From: Jalil Arfaoui Date: Thu, 12 Feb 2026 15:46:43 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20support=20WiFi=201-3,=20ic=C3=B4nes=20o?= =?UTF-8?q?fficielles=20Wi-Fi=20Alliance=20et=20affichage=20par=20ic=C3=B4?= =?UTF-8?q?ne=20dans=20le=20panel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ajout des générations WiFi 1 (802.11b), 2 (802.11a) et 3 (802.11g) dans les types et la détection legacy (fréquence + débit) - Remplacement des icônes SVG WiFi 4-7 par les PNG officiels Wi-Fi Alliance (reverse signature, blanc sur transparent) - Conservation des icônes SVG custom pour WiFi 1-3 (non fournies par la Wi-Fi Alliance) - Remplacement de St.Label par St.Icon dans le panel avec la classe system-status-icon pour un dimensionnement adaptatif - Fallback sur St.Label texte quand aucune icône n'est disponible (génération inconnue) - Mise à jour du build pour copier les icônes dans dist/ --- package.json | 2 +- src/extension.ts | 28 ++++++- src/icons/wifi-1.svg | 1 + src/icons/wifi-2.svg | 1 + src/icons/wifi-3.svg | 1 + src/icons/wifi-4.png | Bin 0 -> 8058 bytes src/icons/wifi-5.png | Bin 0 -> 8862 bytes src/icons/wifi-6.png | Bin 0 -> 9370 bytes src/icons/wifi-7.png | Bin 0 -> 8332 bytes src/types.ts | 15 +++- src/wifiGeneration.test.ts | 146 ++++++++++++++++++++++++++++++++++++- src/wifiGeneration.ts | 39 ++++++++++ 12 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 src/icons/wifi-1.svg create mode 100644 src/icons/wifi-2.svg create mode 100644 src/icons/wifi-3.svg create mode 100644 src/icons/wifi-4.png create mode 100644 src/icons/wifi-5.png create mode 100644 src/icons/wifi-6.png create mode 100644 src/icons/wifi-7.png diff --git a/package.json b/package.json index 86ef91b..3cac7bd 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "tsc && npm run copy-assets", - "copy-assets": "cp metadata.json stylesheet.css dist/", + "copy-assets": "cp metadata.json stylesheet.css dist/ && cp -r src/icons dist/", "install-extension": "npm run build && rm -rf ~/.local/share/gnome-shell/extensions/wifi-signal-plus@music-music.music && cp -r dist ~/.local/share/gnome-shell/extensions/wifi-signal-plus@music-music.music", "nested": "npm run install-extension && MUTTER_DEBUG_DUMMY_MODE_SPECS=1920x1080 dbus-run-session gnome-shell --devkit --wayland", "watch": "tsc --watch", diff --git a/src/extension.ts b/src/extension.ts index e7cb481..f4cc6c1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,6 +5,7 @@ */ import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import St from 'gi://St'; import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js'; @@ -24,6 +25,7 @@ import { GENERATION_CSS_CLASSES, getGenerationLabel, getGenerationDescription, + getGenerationIconFilename, } from './wifiGeneration.js'; import type { GenerationCssClass, ChannelWidthMHz, SignalDbm } from './types.js'; @@ -69,6 +71,7 @@ const MENU_STRUCTURE: readonly MenuItemConfig[][] = [ export default class WifiSignalPlusExtension extends Extension { private indicator: PanelMenu.Button | null = null; + private icon: St.Icon | null = null; private label: St.Label | null = null; private wifiService: WifiInfoService | null = null; private refreshTimeout: number | null = null; @@ -95,6 +98,7 @@ export default class WifiSignalPlusExtension extends Extension { this.indicator = null; this.wifiService = null; + this.icon = null; this.label = null; this.menuItems.clear(); } @@ -103,12 +107,18 @@ export default class WifiSignalPlusExtension extends Extension { this.indicator = new PanelMenu.Button(0.0, this.metadata.name, false); this.indicator.add_style_class_name('wifi-signal-plus-indicator'); + this.icon = new St.Icon({ + style_class: 'system-status-icon', + y_align: Clutter.ActorAlign.CENTER, + }); + this.label = new St.Label({ text: 'WiFi', y_align: Clutter.ActorAlign.CENTER, style_class: 'wifi-signal-plus-label', }); + this.indicator.add_child(this.icon); this.indicator.add_child(this.label); this.buildMenu(); @@ -153,17 +163,31 @@ export default class WifiSignalPlusExtension extends Extension { } private updateIndicatorLabel(info: WifiConnectionInfo): void { - if (!this.label) return; + if (!this.icon || !this.label) return; this.clearGenerationStyles(); if (!isConnected(info)) { + this.icon.visible = false; + this.label.visible = true; this.label.set_text('WiFi --'); this.label.add_style_class_name(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.UNKNOWN]); return; } - this.label.set_text(getGenerationLabel(info.generation)); + const iconFilename = getGenerationIconFilename(info.generation); + if (iconFilename) { + const iconPath = GLib.build_filenamev([this.path, 'icons', iconFilename]); + const file = Gio.File.new_for_path(iconPath); + this.icon.gicon = new Gio.FileIcon({ file }); + this.icon.visible = true; + this.label.visible = false; + } else { + this.icon.visible = false; + this.label.visible = true; + this.label.set_text(getGenerationLabel(info.generation)); + } + this.label.add_style_class_name(GENERATION_CSS_CLASSES[info.generation]); } diff --git a/src/icons/wifi-1.svg b/src/icons/wifi-1.svg new file mode 100644 index 0000000..22ff6ab --- /dev/null +++ b/src/icons/wifi-1.svg @@ -0,0 +1 @@ +1 diff --git a/src/icons/wifi-2.svg b/src/icons/wifi-2.svg new file mode 100644 index 0000000..45db761 --- /dev/null +++ b/src/icons/wifi-2.svg @@ -0,0 +1 @@ +2 diff --git a/src/icons/wifi-3.svg b/src/icons/wifi-3.svg new file mode 100644 index 0000000..2233dcb --- /dev/null +++ b/src/icons/wifi-3.svg @@ -0,0 +1 @@ +3 diff --git a/src/icons/wifi-4.png b/src/icons/wifi-4.png new file mode 100644 index 0000000000000000000000000000000000000000..9e62a954467152f6ce0064b0b0170bc9d65a3143 GIT binary patch literal 8058 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 z z4$1ApA7IuIO6B|%;t#>8k!q;@5hi~M_Yz0i?bD-DI!L#}%&KM!1Lu`;AwSM+Ov!^^ z>kctBICb_|x?EhBmq21X{`gf?*CA)F$lgGjklHc@8rU@@0#eO1V;10O;)c7@h{OZ; zgB92-s(bDcm}n*Sh&}|Cl*E2CeYyh%J1wa|T|+e^c$ns-A{&M0E9M2(BWP1>+2($j!LYLHt2iM7CKfzmaFM zl8)lfs?^8Oh}_(}rP8zBKK;m?C@9D-Zx8b5>#HAP}3lzY2+YQ$PX zp^8*dPewmhPu&Rn67i8lf38(@G!}_T9Xrdg+gJF>@Z7P9S2vWMQ|NsRVCRn0o6)<+eCpQ@g@E$}_U@A)-9n<4 z;5o#XT7N}Cyhd#{BWPNEFY`sowZCU1&%Q_zt!bsZQV}P?cF(HCDZCNd(lw+}1WMik zpeuB6fs>caah3d~XPCmwhZAId#EEqsJ@|done~-n)0&xv7x{Ge^Ow)21EiJSxVbEC zUtNU5_vsLm8daHEZ_DEH`R~t$4!~v_;8LQrN+K^PJ(z9Kjr5}S%^mIxvS{$W^pr&D15*U!g81mU z=6$Pbotm-!Ky3r{p0WlyV4a5^Fu8)jmXXIkvm*%2E0+YSwI4R?Lv9}!OMCmf$j5t3crY?8|1O;S zcnpOfiA$Q!00+N1F06jgfs%Vps;%$jA#YY1@rkP3N$pZ zsxD=N1>Uvz@r&O{R&fQeB}6zGGhbkU{EASHb+WRQXoCkBQzl&%2{pi$M0sf+V!Kui z+-^%XcQkKrCm)}b(j63LIIcsFId%(&J9UYBOFv}gr~36{npT?-n=N^=egf4lHW71x zD$w}fTL+G)6?)8Izk#k+__wZr>6J_`!(JcgI}(sZu}MP&wmXHkV3PHqFS{n#tY1K7sO$Po6JZ0l$|8FCeA6Py!u0j!5L^@x<;8T zE&Zf3NJ9D~jH=#N>(A9&APnzgw(7%Fg{GKhINq$uIHO{9GZ`MCme=|b3Vy#Er0);yvUu{`YhmgiP!lj4=PM|Wc&&Sed1>$YStH+a} q`GL)6*Mlvv{ATN_ZWlr}8?(e49p@jcMn!l`C(_o?Q?CWv|Mx!#0@29; literal 0 HcmV?d00001 diff --git a/src/icons/wifi-5.png b/src/icons/wifi-5.png new file mode 100644 index 0000000000000000000000000000000000000000..c40e278ea8c4b19b31d7499b4f246970aac8dac6 GIT binary patch literal 8862 zcmX9^bzGC*_a7Y+qlD2V-Q^G%Eer(&ln%+!GDeruqf2^}fFh-UB11%CfYb=3djg}S z1PSrC&-eGo^W5j2dtT?p>pAzl&->n_M@D+I)a=v%0Du;%uVV@T5WxtKD02QH04ah09-@$H^&kL%7Xa1Kd>lfwU-CEb`_}h_zVJUQH&1}i$d1f`W<+z; zPGb74#9Cx=)G{nUv97mI_o=5F39n=BZq;(jY)LBz`4ih2WypK#TS*Y#rLhrJ`FEvd zWlR9~lB5XJ*U;=#`F~xO=}%lk5AjOJP^&x8=AXwGBye(Y25+Zk2<-TiEo}hg98_WN zO3HbpM2(~ID=I`DJ|+znA%f)dE)J(Yeb;u-4($sq6&&nX|2!g9mORU1yZ*`BI?PWhlG&h&*L%;x2_? z&{9^i^hue#i*Hb>8Z4#^o#|bmyLSSoGmh8LyP!0jZM@I@nignAZ@rSn>b(;X+}yK; zCCeVzx<^rmi%9*e*%}7rRJ8`K`)bsLcU6LUoJ-*I4|)Wp3Jqey(|ON(taZXPQ`_yF znDG2(g_EyXT<;5kQdBwx5>!Q2bgFm+AQTPJ;QW)gr**;&AKHYyE7BGAj$Rb|lH+VM z-aOQI(bad6apNDFR*J=ZV?~>*iYscOiks9#a07P1!?xU~1zuSV$A*gvEKgW8Q46lm zA7L^qKasrJOB6=uAy27*cwf!>;%Jd8vMfmVKdygr3WA2FUktO?owEi1>HDvn=)igg zC0(UthsG#dY$hYRgBkEvRrpmAB;qS+laVgofuWq@tu8GC6|;{Rb0>4;WmdkB<7wn%AF3hvhKJ{)=9Z^_;NTr`Db z?aShot50er5q$cA-xQzu7r)y#ZFxZQL3gkq?Z3#psh2Q>KQm^IT)^rqQcYjX+V&3(f=Ce_xGz*{rOD(HMK1k_|!A4&;2 zUISB7?xP|Z;=(Fx6sSJY`1o`DPr$giojK~(k6VWik1KWSu7pEckS)fYG`7hKIjp@4 zixz-+0n97AgIgM?zp@`>R#NJ^CAE2MlQljsxTb)wob(;JA53PnyKWU{y>x?w+&e+G z@RX7>Z@!I_)xYe{jB}|-@ub7dQS1`s2O{P~G}uTK{6z0}Ut_f}a}XP77p-ia4_|dS zpok5M91VC*P*nxoifw0dV z92y1MxQx=_r%4G`v!I*Ajg%eU+G`)zJT{c7T3IiD2 zkVI3&Y2aPh(tj-p1&_qXkuAPwYX56u$o;SB^%(es#xVg28eHdA3?ww$cm8IBl@6s* z0p(eXlxlgg_vX&nuut(kUC*?*PFD%_?Yh!Xi33hY@;{^132sni3k34}&!+t(hO#`f zxMINmOP-XoG5Q^|cJVjgxwF~lLD4cKoOe>cuw!(;hDAkTxm084y8p?Il1>d2GUm$0 z_`STVhTi-@z3zM<0$@JE1NK90nDB^#Z^DDfE5Q>q+YSc@vQr5527eh&(0JLzYPS=J z8_y*8^lG~>McLaHL30Pz=lpapNO?8XrX!!3PDo+j6u9cBAlv%h4}RvxSvxbn0Hpo- z0TUTd>U-<_7rC z3z2`&V&=q%mvcL&dbRYX_rSSO2L&p(3rkWLz=k%jWm+X3w7(Nv6?+9fSL z)ArR%mR)0Y3->ZeD$3d?79w$*@Wmx&HWa^UI=F$`{qqE(ax`-H~t1N?7O?9Fn2k)Y%9k-wtaTwAB828-Ta{w(}vUgCQ+re$7N` z{Bf=HGE{vtvU}y`NAKj7)MS{O7W??!Pj>M>`jkOq4^h8Ac6fX~D9h^Py&2?6i?ztQ zS)4})b2}{rF)Vi}2alPe_H=9mXAaW+$Thy&2AH6u!RHTV;&+>#zyR)Rt^4yZ%p83V zsjXWRdTZL$FhihCiG5jmSm+Vt1wR?{#eNh*+Pp{ zB>A(dC3I9q3ZEWp+vy_(Fg^As3@xfrw2Z{BZ$WJI4+HFPCZ#H^VMZ6baG4jz>Mj#w zG(bvO6#EXLr@@3N+RyYIi{!b2z8JFAXEFC!AVRhH)=a+FuFTQ_-qBl<$JOVHst}8t z?LYv`hyd4A@b1Qz4U4_TcY*ic`kLJI3I3Vh`Gly8tXhZ$K0O$hnV?)#8;uZAbVRlo z8A!7+YdxcVZ{Xoh0-vVB+0tV`5@vro{@?53G@;=h*8_KI`s$+pMK*#I9PQRi;n>S^ z`)O`8#jSGkd|gAr_#>I6;N>qP=U4$WS@Ny~=k{l%O79_OKGeXz{L(nf^wcIhz>3Qo z@a^XO_4>bPf02ro!NIc(fbh4%=GwFjDFdBKPanLsPIx~2Ud8Coimri&2gK1-K$!bb z{G#4h8XpI{Z7KP$&&9uTxBuo{9Zp3KY<4ohUW63V@nN4(65Fr$&m0@RlY6xk34h{Fa-q!daX^dgxsh25dXV-G-cw9kizo<7|yzelG7eaWK zFnB$P871+}w@nl40(BtSLr`&ywn66a%jC4aJT7`6xJ(0Rc`D-k7Gboa{|TBMk?Ygy zA>zWffBC^>9*)IpqJ=_m`E;0j+J{=GK))HAP#ZP6t$GIh$v7|8R>eKV!7Ym`MxDxu z3-#MT34aRm6sZ-U;7DyV8fW}!e~LuN=9?z|wX;2~jMHd0xCTBWe<4`Rtb;O^&t~i# z<7$m}Nu0ZQ|CzbtNShvr5A#;J3@6wIg)vdral3P1E@;{fAV)7qG*0k9=QC$`6NKwDX1kf>=flo? zk4vSFQQm3!i`8b@Xz)yB z3Pn0CO{?7qXc2;0-blg09iaKW@+{=#qX8bmA|D-VgWOWV^z>BSCj@{4 z6KJ3YZbxgP>RJxAe%;}tWFJgcSWCYt~BXb8!Gz69Qr^0H=s}BiAw~_;XPE%kHLJ9gTVaf}u zrHB25@2LvSEGb`Rdh_`F5rp2VAQBM>ghh7bSId*!3TUYct5o8^gobSGO{ESCLT?yY zN54sbS?r4<_9RT0BPPYI2yLVk@V<@$M^maF?)=<1d!2~2fzh8-Q3F+nn3`C5Fr|65 zh>@pgH^|?pa}?h{c-2C57wirG2wb*mzt92C`ogjel#{dtA(e9=m#K zT7iSHc4JDaf2hfq?vwrp>ofi(g}KCsi~Uo>UN5&J|IKyx=1>pJEShY^7&D&l)k+UM zhUBC+Dc|Khd&#g{eZ5m%rf-ctk_~2VzJBKeZ`3lce^a;;%<|nQ=Pp;3FoyqryBX~g9*rhsW zx$GIjVLxA(3TmCU)<~I{m(7k$Qi4l4haG~3T%T{%aDKz+`suTAz}l?94WJtISAb9bS>lNTGtD@tkieX8~y)sH{_Hv7bg;nQRf zRJXFTddA7c`&T-^tc2762-{-c6x$)g*Rx?j?XT-R1>F0&@%6Pgp38V4c@M*mRej<&CF*RI$Mk|b&_=Rt)?h|oBpi%N~0`SuLH!5qQreea$ zk%>KcF8DNbt(du-tcHA&0_Tm?Qy5 zFwrmWkBX&H%?|DeY}2!3(MT)<>3J@|$wX-fGQ18hNJ zF4O<^u0YyGFs|G=Rog3K&S5hh)BUq4^L`At7;tWz@ddo;eD}uaW@H8Z?vHDo%){FV z{h#Xy0^+8CHx2}*CC4dn|Ah(GF{OR~cl)t9AVjMo%?Vf8~AbOcosm8T6H zj+>HCv=8)&2=K#i?9IjGs%pH}gF4g` zuC#2)UHS?AZg#x{mmi*rJE`6oX2g3_EFxqF#N@G#!p~PYD4N_ydlokpTXZ&dqrnRm zrYJGFv*S#jx)E@G^$(rrGVD9!5+l$mv#yyye|}^OSI{Z)n{1AsasFb8zK!yEcv6X% zgGb*M$_pgngl*N=#jVU5$H>IVqmY(_9OzZ-d-3L7R&%pSomKUruLG@fLx0Ucs1`|) zx~suA(Wtfim|yf4+#ljpT5)+-me3E;(>qyGZ|z{|h*rHzKvc#b{J+=kP}RI&8)4-; z=^rI>8}6hy*Qw5@cs1s7Xrg?wg;*N-uhBEd{*%)(5M|)bX*lP`jgCrO)nw1s1sa zH%o8j?q>wC|G zGnvC0l`;#%gh4IW1)W1H0-v-ilG=opGq~|!%qu1g@^#WwNc9+Zqok?c!O(5zSxOI- z`>$v z;N6UD8k;0UYN(|PuF9dCsb?=)moO9dfzOK*@<-!;M7SEF#HkX~C<9aXDf1_Td*m$m zvCco$DQIS%x#+X!V!51T>rHZHsFywIV+eWNnCDW%qqmM6(&vs`^PIV7Bx91=(sZ1^ zBbTCT$>+dtg}=Pa)!3^LOG)ix?h_n=7O$mhNc=@fy96G-Q#~!c8iatiFH$uY)-@aL z9!6|A1#&yL{8ebYP&SL%oG=_KN&Uk!I3(P^xpzCMA^1K&Uz?G6ovg7I2s$g!=3xxg zkzPydf?aymz_=@9gzeZV`XB#YH(-B}D67L=OV$y0m-udTz-j5-_up*o%bO;kV1XCl zuj^XA&8w;nDIf9=@2{G2C$K%Z^O3PmCipg%k{8>TDj5BzOyW&L`^U82kbQk~L{>q@-2nXvV z=PnWJ5{GlBXn(==1I*TGHtIfVmq};d^kEoY<_5K4j-qMW@+}+;O>an7et+Q90tF#Y zE;{b!fo*?>I@4+(b*0~KZN-IKRbyIDq{qX>Z7^sMAG}R2JLEyWbD6(hB>avzhZfhq zOzxG+^dJAH%faDJ7&Mm_)$*i9%nsJ-F9P%_-GD#wsf2VJA%k`%h0sh4-G=+-i(6fK zNT0OlHAXIjKs4$eyloglhKI@2PGG?Mwq+IDc%=pl6Ync(hEVd`rST4x38YKHwiSs& zC~=Ei0)^ABKDJn=s_ZxO`-CD)7F=}adTUygX6cs9IN=AS;zDF^Nh_=Iccwo_AQZSNiZ#WmpcjX4-)r3{y9BPer6(pT|j47kU(w#(~d= zs|9I3qPN1|*mUdy7f)wMDq6YAu++!TkNJ~*S{=BSJwRUg(1h}!>Em(tqGxQ*EvI7j zcdi*A^DqwVI>HcSE4l2k!;ucAA6qdst|cSARImws+bHLx_Txd2(9zGg*W9uY`X@ao zUaV)xWaf)+hu3&_=&)nyB3q4=ZoQAQZq zV%;dH^wtr!#u-fxt6}}m+NgfeZO_>+2^z7i4=E9Z$gK-2w)^x^6SaSA)m@eQ-m33v-12>| z{W1Th@F*dzzNitriV&tJg}bw5%Y4B+)a{p@TxQS7hO7OIT{k-$GxV5jNR~F(sd#hz zbUcUTaK#I2Vtu?mM$v5Xz;hKc-PKek*K zE#q`W)*0B1XvcUmy9)SIU9SL9tZrPw>YBynMC(}C*4u875aZ@*AC1q6sC@bKoye3E zJ|=jWV?CWz9hU-C06#s0T5km{A*v)U@82DJDDx|>qVFP8=6sLcVu{U%*svK`^r}yC z1zP>1wW{D1%ZDUH-rJ56;Lgc}(0J0~?0J25Il1+szp0G#$ylS)a?viz;+WTXoiyTA zdaF`CwHJhEy>Iv>OHB_2+ChODG3grr(hYCh{}BZw&otM2P`Jc5!#`c0x|%7BhJ8Hk z6Jgw}8Xiy3h0{G;$1OGKG{%`na}O)7-_zoE%3r?4PR=FFlE@XzIRCyQGF+f(xXGK# z9)CmXf;O?P`I@r}cAYo)u_w<*^PmS6;2I{Og6DTTE;qBVNk5ZSGYd`fqy4GrJnD}` zSXhk)GR_}P{}DL_MQMuj%ccJsg96n@?2t9USF#@Z#vbGYj)g1kReq=*C ze&8@7i?uU>EAox|XTBM;;i`7)F_tixO-V>)CQ`8kHa zEcXpxKldpi!Cc&J`t)%TmKVa^hK&I3oXsV7Nv1Bz&m5}-2O589%>clopnE=&l0uZm zvRI^oT5qxAlT0Qwy(vjh@s+{n2ru!oR4^-3OUm?oY^7wgmf;lEW13ODRpeVV#Bj#m zo-|DpHJmi3>$QSgy8V6R1x0p}`dkW|`p~=iovdBCxQVdu_qCV_0KA@sYZ*88q|g=e zU0;jW2914i5rW>-E~+!mF7PEK0C6^9Xn3mnOy2_OMdW>?#c|U?Dm|5?dm_z)#s&9` z7hntFei=P+x-O8J=-jQZM$GMd4afrn`^MpsUuyC>u*cd`6-U0B7&N!zzsQGXma0G0 z&vj>C$mE8*My!?>CI3-6>G~zdhfIqIVn}$!7^(>o376u)`vCDL$hSq#(s!SatkI4S zf0-1$msVFwkZ!jZ*fBLs`4WGKI}>tCd$8l=74btTqd7IL;s|Sl0lk#TVY{^02;8ZB z4JC9761O@YkooF2VGdX80zF*kzV@y0d~L!ch(Rl>pedKh!k7p9naB0oJ+Ed~qhjyZ z84`j|j7E4Fw_ylLLYF6*fk0vg_|t$W%MEW=}73y&w^-aaVXlROi*NLmzp+@WxyC~U``D3qt{~ABp^D4S|?T#G$j~w)Q=|6wH z-zOE=9XS?!uVeL9?9JP{5!$vC7fjen75xwO^r+Ii2}_0yb>zRdcVqDzwri!_$i1rj zU*3J%%~I$P(l!}x>%ekdY_B#OzDi-gPeG)9-4$AeKlnwUa_%WVKgTX2+716A4%`*# z$f~wNcZ$3cao%uki-zPhetd*fxU%C~tmO>M!yPg&s)|;u8{X=gcTAb_0v&s%4Sn`l zcu1M1Y!Rzi(-k8y|j;Ck(vZzI|dXR)Sz)WvpCA7N6Z_Rmm@@gbbEms@VjM z!2<7_rR(BVYikkKL6ix9PQM~gr3uJSbpu4{v(@9NYgwIN)xW=hJbkA6ft5?y+SG>L zCnQzH#{2nGa}fZO;2PKF+o1aNVwW@5tK5H(9hIA;y>PQ?ia6VnTX_O!EIu*au zL|Khc#T(i8yqI)qpB`CrM$)Mot#rb{+4#)(quG{xPND-V`U5Qv?mlG(*s2=FQTc4| z&Iip!g0K}H&VmK%XAeUJ97>f*kQPoqKItL$+zx;3V!p1T_3f$Vu=}*eCk*eiB6FRw9XH4 zDE(m*`W#)lrkzshj3rcppV}}McsnPix^7?=qntG;w(L8k*XtTsefJGteS#7Y)`te5^HJeu%YV`^gnA=?CNn5+cF)FiXTkE5bQnveF?(n-m-j_2K6`;nC6+K z58zkzg-WqWz|;MEwj1e`vMC6h-+B3zAMHUX*om{iJ7%`;VrcF_6B7!qnH`VrTlZd5 zMB1U4U`_b6i6R?%ocBjl0A5u!9%_ZTfQ@_~-m1dXgr5O}Tdj?P{rMARCF~P>!g=5~ zgOPI!!@NHh=_pjl);NK9fmF5yb#Rt7y}!^I}5j*1-U)@XvF!j(rCYc%BW$kiHtzP=C|8b1ZFj=)6yaM0{G)yfzq@J5+HkK4~%TJIh`?CbXnsSRxX6Ejls9D`;vRvgP?%Vk;T=Jd=Y)7hdN=bZMo7sD65oh4*Sp_6QnOlqFcHdms$J8fyl#Mw@f1eIB28~)A zltcCv|7ee`MEQo;KGY$E=un$W7L`4kydbM!KQPRl408dd;o0Y*Z;DD;Nc_@=YADQJ zywF2sXuDTFS3PA7Qgr_zGA*X$FV0HPL^HVoQ)C|vZC@t7PmlxRe~yd#u!+3cEz9cu zHK&L?@u*K!lDNGtKSNjK)agkMhtU4axg2N*e|K;WsgKX|uM5CjC~(_yQq@LPypvZT zD5O!~y?x&B@)J$Og3A&llzznLQvXk$DAB5jM~Q#COK#JaIB?DEi$Sxu;iE)37f4F9 zb5r?&c+b^!UUI@JhB^{6;5{u_VNT_M%g_pW!r39E*J;LY#LY_;;zuMHW$t<(9Y;9| z9a!$#~k0j|CA($R@NolU4IEdlo4XZd_Py6uM*kuxK zY}UuJ?n`9{wrfzw?~0r9)$p3$UngaPga1ex5bJ1z#hP5gg%bc&*GQ*P>uJpY0h)CP A>;M1& literal 0 HcmV?d00001 diff --git a/src/icons/wifi-6.png b/src/icons/wifi-6.png new file mode 100644 index 0000000000000000000000000000000000000000..8cbe716cef86a4027666233e6f7c5d7f807495ae GIT binary patch literal 9370 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 literal 0 HcmV?d00001 diff --git a/src/icons/wifi-7.png b/src/icons/wifi-7.png new file mode 100644 index 0000000000000000000000000000000000000000..dca98ae08b60437aa7633c506d95727a8788c4bd GIT binary patch literal 8332 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 literal 0 HcmV?d00001 diff --git a/src/types.ts b/src/types.ts index 0e5f630..49bb545 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,6 +17,9 @@ export type GuardIntervalUs = Brand; export const WIFI_GENERATIONS = { UNKNOWN: 0, + WIFI_1: 1, + WIFI_2: 2, + WIFI_3: 3, WIFI_4: 4, WIFI_5: 5, WIFI_6: 6, @@ -25,11 +28,14 @@ export const WIFI_GENERATIONS = { export type WifiGeneration = (typeof WIFI_GENERATIONS)[keyof typeof WIFI_GENERATIONS]; -export function isKnownGeneration(gen: WifiGeneration): gen is 4 | 5 | 6 | 7 { - return gen >= WIFI_GENERATIONS.WIFI_4 && gen <= WIFI_GENERATIONS.WIFI_7; +export function isKnownGeneration(gen: WifiGeneration): gen is 1 | 2 | 3 | 4 | 5 | 6 | 7 { + return gen >= WIFI_GENERATIONS.WIFI_1 && gen <= WIFI_GENERATIONS.WIFI_7; } export const IEEE_STANDARDS = { + [WIFI_GENERATIONS.WIFI_1]: '802.11b', + [WIFI_GENERATIONS.WIFI_2]: '802.11a', + [WIFI_GENERATIONS.WIFI_3]: '802.11g', [WIFI_GENERATIONS.WIFI_4]: '802.11n', [WIFI_GENERATIONS.WIFI_5]: '802.11ac', [WIFI_GENERATIONS.WIFI_6]: '802.11ax', @@ -64,10 +70,13 @@ export const SECURITY_PROTOCOLS = [ export type SecurityProtocol = (typeof SECURITY_PROTOCOLS)[number]; -export type GenerationCssClass = `wifi-gen-${4 | 5 | 6 | 7}` | 'wifi-disconnected'; +export type GenerationCssClass = `wifi-gen-${1 | 2 | 3 | 4 | 5 | 6 | 7}` | 'wifi-disconnected'; export type SignalCssClass = `wifi-signal-${Lowercase>}` | ''; export const GENERATION_CSS_CLASSES = { + [WIFI_GENERATIONS.WIFI_1]: 'wifi-gen-1', + [WIFI_GENERATIONS.WIFI_2]: 'wifi-gen-2', + [WIFI_GENERATIONS.WIFI_3]: 'wifi-gen-3', [WIFI_GENERATIONS.WIFI_4]: 'wifi-gen-4', [WIFI_GENERATIONS.WIFI_5]: 'wifi-gen-5', [WIFI_GENERATIONS.WIFI_6]: 'wifi-gen-6', diff --git a/src/wifiGeneration.test.ts b/src/wifiGeneration.test.ts index 393654f..21cf4fe 100644 --- a/src/wifiGeneration.test.ts +++ b/src/wifiGeneration.test.ts @@ -7,6 +7,7 @@ import { GENERATION_CSS_CLASSES, getGenerationLabel, getGenerationDescription, + getGenerationIconFilename, isKnownGeneration, } from './wifiGeneration'; import { GUARD_INTERVALS } from './types'; @@ -37,6 +38,9 @@ describe('createEmptyIwLinkInfo', () => { describe('isKnownGeneration', () => { it('should return true for known generations', () => { + expect(isKnownGeneration(WIFI_GENERATIONS.WIFI_1)).toBe(true); + expect(isKnownGeneration(WIFI_GENERATIONS.WIFI_2)).toBe(true); + expect(isKnownGeneration(WIFI_GENERATIONS.WIFI_3)).toBe(true); expect(isKnownGeneration(WIFI_GENERATIONS.WIFI_4)).toBe(true); expect(isKnownGeneration(WIFI_GENERATIONS.WIFI_5)).toBe(true); expect(isKnownGeneration(WIFI_GENERATIONS.WIFI_6)).toBe(true); @@ -226,8 +230,118 @@ describe('parseIwLinkOutput', () => { }); }); +describe('legacy WiFi detection', () => { + describe('WiFi 2 (802.11a) - 5 GHz legacy', () => { + it('should detect WiFi 2 for 5 GHz without generation markers', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + SSID: LegacyNetwork + freq: 5180 + signal: -60 dBm + tx bitrate: 54.0 MBit/s + rx bitrate: 48.0 MBit/s`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.WIFI_2); + expect(result.standard).toBe('802.11a'); + }); + }); + + describe('WiFi 1 (802.11b) - 2.4 GHz low bitrate', () => { + it('should detect WiFi 1 for 2.4 GHz with bitrate <= 11 Mbps', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + SSID: VeryOldNetwork + freq: 2437 + signal: -70 dBm + tx bitrate: 11.0 MBit/s + rx bitrate: 5.5 MBit/s`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.WIFI_1); + expect(result.standard).toBe('802.11b'); + }); + + it('should detect WiFi 1 for 2.4 GHz with 1 Mbps bitrate', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + freq: 2412 + signal: -80 dBm + tx bitrate: 1.0 MBit/s`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.WIFI_1); + }); + }); + + describe('WiFi 3 (802.11g) - 2.4 GHz high bitrate', () => { + it('should detect WiFi 3 for 2.4 GHz with bitrate > 11 Mbps', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + SSID: OlderNetwork + freq: 2437 + signal: -55 dBm + tx bitrate: 54.0 MBit/s + rx bitrate: 36.0 MBit/s`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.WIFI_3); + expect(result.standard).toBe('802.11g'); + }); + + it('should detect WiFi 3 for 2.4 GHz with 12 Mbps bitrate', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + freq: 2462 + signal: -65 dBm + tx bitrate: 12.0 MBit/s`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.WIFI_3); + }); + }); + + describe('no legacy detection when generation already known', () => { + it('should not override WiFi 4 detection with legacy', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + freq: 2437 + signal: -65 dBm + tx bitrate: 72.2 MBit/s MCS 7 20MHz short GI`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.WIFI_4); + }); + }); + + describe('no legacy detection without enough info', () => { + it('should remain UNKNOWN without frequency', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + signal: -65 dBm + tx bitrate: 54.0 MBit/s`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.UNKNOWN); + }); + + it('should remain UNKNOWN without bitrate on 2.4 GHz', () => { + const iwOutput = `Connected to aa:bb:cc:dd:ee:ff (on wlan0) + freq: 2437 + signal: -65 dBm`; + + const result = parseIwLinkOutput(iwOutput); + + expect(result.generation).toBe(WIFI_GENERATIONS.UNKNOWN); + }); + }); +}); + describe('getGenerationLabel', () => { it('should return "WiFi X" for known generations', () => { + expect(getGenerationLabel(WIFI_GENERATIONS.WIFI_1)).toBe('WiFi 1'); + expect(getGenerationLabel(WIFI_GENERATIONS.WIFI_2)).toBe('WiFi 2'); + expect(getGenerationLabel(WIFI_GENERATIONS.WIFI_3)).toBe('WiFi 3'); expect(getGenerationLabel(WIFI_GENERATIONS.WIFI_4)).toBe('WiFi 4'); expect(getGenerationLabel(WIFI_GENERATIONS.WIFI_5)).toBe('WiFi 5'); expect(getGenerationLabel(WIFI_GENERATIONS.WIFI_6)).toBe('WiFi 6'); @@ -241,6 +355,9 @@ describe('getGenerationLabel', () => { describe('getGenerationDescription', () => { it('should return full description with IEEE standard', () => { + expect(getGenerationDescription(WIFI_GENERATIONS.WIFI_1)).toBe('WiFi 1 (802.11b)'); + expect(getGenerationDescription(WIFI_GENERATIONS.WIFI_2)).toBe('WiFi 2 (802.11a)'); + expect(getGenerationDescription(WIFI_GENERATIONS.WIFI_3)).toBe('WiFi 3 (802.11g)'); expect(getGenerationDescription(WIFI_GENERATIONS.WIFI_4)).toBe('WiFi 4 (802.11n)'); expect(getGenerationDescription(WIFI_GENERATIONS.WIFI_5)).toBe('WiFi 5 (802.11ac)'); expect(getGenerationDescription(WIFI_GENERATIONS.WIFI_6)).toBe('WiFi 6 (802.11ax)'); @@ -252,8 +369,30 @@ describe('getGenerationDescription', () => { }); }); +describe('getGenerationIconFilename', () => { + it('should return PNG filename for WiFi 4-7', () => { + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_4)).toBe('wifi-4.png'); + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_5)).toBe('wifi-5.png'); + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_6)).toBe('wifi-6.png'); + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_7)).toBe('wifi-7.png'); + }); + + it('should return SVG filename for WiFi 1-3', () => { + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_1)).toBe('wifi-1.svg'); + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_2)).toBe('wifi-2.svg'); + expect(getGenerationIconFilename(WIFI_GENERATIONS.WIFI_3)).toBe('wifi-3.svg'); + }); + + it('should return null for UNKNOWN', () => { + expect(getGenerationIconFilename(WIFI_GENERATIONS.UNKNOWN)).toBeNull(); + }); +}); + describe('IEEE_STANDARDS', () => { it('should map all generations to their IEEE standards', () => { + expect(IEEE_STANDARDS[WIFI_GENERATIONS.WIFI_1]).toBe('802.11b'); + expect(IEEE_STANDARDS[WIFI_GENERATIONS.WIFI_2]).toBe('802.11a'); + expect(IEEE_STANDARDS[WIFI_GENERATIONS.WIFI_3]).toBe('802.11g'); expect(IEEE_STANDARDS[WIFI_GENERATIONS.WIFI_4]).toBe('802.11n'); expect(IEEE_STANDARDS[WIFI_GENERATIONS.WIFI_5]).toBe('802.11ac'); expect(IEEE_STANDARDS[WIFI_GENERATIONS.WIFI_6]).toBe('802.11ax'); @@ -264,6 +403,9 @@ describe('IEEE_STANDARDS', () => { describe('GENERATION_CSS_CLASSES', () => { it('should map all generations to CSS classes', () => { + expect(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.WIFI_1]).toBe('wifi-gen-1'); + expect(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.WIFI_2]).toBe('wifi-gen-2'); + expect(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.WIFI_3]).toBe('wifi-gen-3'); expect(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.WIFI_4]).toBe('wifi-gen-4'); expect(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.WIFI_5]).toBe('wifi-gen-5'); expect(GENERATION_CSS_CLASSES[WIFI_GENERATIONS.WIFI_6]).toBe('wifi-gen-6'); @@ -272,8 +414,10 @@ describe('GENERATION_CSS_CLASSES', () => { }); it('should use template literal type pattern', () => { - // Type check: all values should match the GenerationCssClass type const classes = Object.values(GENERATION_CSS_CLASSES); + expect(classes).toContain('wifi-gen-1'); + expect(classes).toContain('wifi-gen-2'); + expect(classes).toContain('wifi-gen-3'); expect(classes).toContain('wifi-gen-4'); expect(classes).toContain('wifi-gen-5'); expect(classes).toContain('wifi-gen-6'); diff --git a/src/wifiGeneration.ts b/src/wifiGeneration.ts index 7aa08e9..5beed42 100644 --- a/src/wifiGeneration.ts +++ b/src/wifiGeneration.ts @@ -81,6 +81,8 @@ export function parseIwLinkOutput(iwOutput: string): IwLinkInfo { parseLine(line.trim(), result); } + detectLegacyGeneration(result); + return freezeResult(result); } @@ -101,6 +103,28 @@ function createMutableResult(): MutableParseResult { }; } +const WIFI_1_MAX_BITRATE = 11; +const FREQ_5GHZ_START = 5000; + +function detectLegacyGeneration(result: MutableParseResult): void { + if (result.generation !== WIFI_GENERATIONS.UNKNOWN) return; + if (result.frequency === null) return; + + if (result.frequency >= FREQ_5GHZ_START) { + result.generation = WIFI_GENERATIONS.WIFI_2; + return; + } + + const maxBitrate = Math.max( + (result.txBitrate as number | null) ?? 0, + (result.rxBitrate as number | null) ?? 0 + ); + if (maxBitrate === 0) return; + + result.generation = + maxBitrate <= WIFI_1_MAX_BITRATE ? WIFI_GENERATIONS.WIFI_1 : WIFI_GENERATIONS.WIFI_3; +} + function freezeResult(result: MutableParseResult): IwLinkInfo { if (isKnownGeneration(result.generation)) { result.standard = IEEE_STANDARDS[result.generation]; @@ -285,3 +309,18 @@ export function getGenerationDescription(generation: WifiGeneration): string { ? `WiFi ${generation} (${IEEE_STANDARDS[generation]})` : 'WiFi'; } + +const GENERATION_ICON_FILENAMES: Record = { + [WIFI_GENERATIONS.WIFI_1]: 'wifi-1.svg', + [WIFI_GENERATIONS.WIFI_2]: 'wifi-2.svg', + [WIFI_GENERATIONS.WIFI_3]: 'wifi-3.svg', + [WIFI_GENERATIONS.WIFI_4]: 'wifi-4.png', + [WIFI_GENERATIONS.WIFI_5]: 'wifi-5.png', + [WIFI_GENERATIONS.WIFI_6]: 'wifi-6.png', + [WIFI_GENERATIONS.WIFI_7]: 'wifi-7.png', + [WIFI_GENERATIONS.UNKNOWN]: null, +} as const; + +export function getGenerationIconFilename(generation: WifiGeneration): string | null { + return GENERATION_ICON_FILENAMES[generation]; +}