diff --git a/src/extension.ts b/src/extension.ts index 330665a..ae74c41 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -30,10 +30,12 @@ import { } from './wifiGeneration.js'; import { getSignalQualityFromPercent, + getSpeedQuality, type GenerationCssClass, type ChannelWidthMHz, type SignalDbm, type FrequencyBand, + type SpeedQuality, type WifiGeneration, } from './types.js'; @@ -63,6 +65,15 @@ const SIGNAL_QUALITY_BAR_COLORS: Readonly> = { Poor: '#e01b24', }; +const SPEED_QUALITY_COLORS: Readonly> = { + Excellent: '#c061cb', + VeryGood: '#62a0ea', + Good: '#33d17a', + OK: '#f6d32d', + Weak: '#ff7800', + Poor: '#e01b24', +}; + type MenuItemId = | 'bitrate' | 'channelWidth' @@ -851,9 +862,6 @@ export default class WifiSignalPlusExtension extends Extension { if ((ap.bandwidth as number) > 20) { detailParts.push(`${ap.bandwidth} MHz`); } - if ((ap.maxBitrate as number) > 0) { - detailParts.push(`${ap.maxBitrate} Mbit/s`); - } const detailsLabel = new St.Label({ text: detailParts.join(' ยท '), @@ -863,6 +871,17 @@ export default class WifiSignalPlusExtension extends Extension { }); infoRow.add_child(detailsLabel); + if ((ap.maxBitrate as number) > 0) { + const speedQuality = getSpeedQuality(ap.maxBitrate); + const speedLabel = new St.Label({ + text: `${ap.maxBitrate} Mbit/s`, + style_class: 'wifi-nearby-ap-speed', + y_align: Clutter.ActorAlign.CENTER, + }); + speedLabel.set_style(`color: ${SPEED_QUALITY_COLORS[speedQuality]};`); + infoRow.add_child(speedLabel); + } + const quality = getSignalQualityFromPercent(ap.signalPercent); const signalColor = SIGNAL_QUALITY_BAR_COLORS[quality] ?? '#ffffff'; diff --git a/src/types.ts b/src/types.ts index 8a60504..129c6f0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -51,6 +51,9 @@ export type FrequencyBand = (typeof FREQUENCY_BANDS)[number]; export const SIGNAL_QUALITIES = ['Excellent', 'Good', 'Fair', 'Weak', 'Poor', 'Unknown'] as const; export type SignalQuality = (typeof SIGNAL_QUALITIES)[number]; +export const SPEED_QUALITIES = ['Excellent', 'VeryGood', 'Good', 'OK', 'Weak', 'Poor'] as const; +export type SpeedQuality = (typeof SPEED_QUALITIES)[number]; + export const SIGNAL_THRESHOLDS = { Excellent: -50, Good: -60, @@ -186,6 +189,24 @@ export function getSignalQualityFromPercent(signalPercent: SignalPercent): Signa return 'Poor'; } +const SPEED_THRESHOLDS = { + Excellent: 1000, + VeryGood: 300, + Good: 100, + OK: 50, + Weak: 20, +} as const; + +export function getSpeedQuality(bitrate: BitrateMbps): SpeedQuality { + const mbps = bitrate as number; + if (mbps >= SPEED_THRESHOLDS.Excellent) return 'Excellent'; + if (mbps >= SPEED_THRESHOLDS.VeryGood) return 'VeryGood'; + if (mbps >= SPEED_THRESHOLDS.Good) return 'Good'; + if (mbps >= SPEED_THRESHOLDS.OK) return 'OK'; + if (mbps >= SPEED_THRESHOLDS.Weak) return 'Weak'; + return 'Poor'; +} + export function createEmptyIwLinkInfo(): IwLinkInfo { return Object.freeze({ generation: WIFI_GENERATIONS.UNKNOWN, diff --git a/src/wifiGeneration.test.ts b/src/wifiGeneration.test.ts index e735db8..150e807 100644 --- a/src/wifiGeneration.test.ts +++ b/src/wifiGeneration.test.ts @@ -11,7 +11,13 @@ import { getGenerationIconFilename, isKnownGeneration, } from './wifiGeneration'; -import { GUARD_INTERVALS, asSignalPercent, getSignalQualityFromPercent } from './types'; +import { + GUARD_INTERVALS, + asBitrateMbps, + asSignalPercent, + getSignalQualityFromPercent, + getSpeedQuality, +} from './types'; describe('createEmptyIwLinkInfo', () => { it('should create an object with all null values and UNKNOWN generation', () => { @@ -645,3 +651,53 @@ describe('getSignalQualityFromPercent', () => { expect(getSignalQualityFromPercent(asSignalPercent(100))).toBe('Excellent'); }); }); + +describe('getSpeedQuality', () => { + it('should return Poor for 0 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(0))).toBe('Poor'); + }); + + it('should return Poor for 19 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(19))).toBe('Poor'); + }); + + it('should return Weak for 20 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(20))).toBe('Weak'); + }); + + it('should return Weak for 49 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(49))).toBe('Weak'); + }); + + it('should return OK for 50 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(50))).toBe('OK'); + }); + + it('should return OK for 99 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(99))).toBe('OK'); + }); + + it('should return Good for 100 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(100))).toBe('Good'); + }); + + it('should return Good for 299 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(299))).toBe('Good'); + }); + + it('should return VeryGood for 300 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(300))).toBe('VeryGood'); + }); + + it('should return VeryGood for 999 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(999))).toBe('VeryGood'); + }); + + it('should return Excellent for 1000 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(1000))).toBe('Excellent'); + }); + + it('should return Excellent for 2400 Mbit/s', () => { + expect(getSpeedQuality(asBitrateMbps(2400))).toBe('Excellent'); + }); +}); diff --git a/stylesheet.css b/stylesheet.css index 1dffcce..57f8342 100644 --- a/stylesheet.css +++ b/stylesheet.css @@ -211,6 +211,12 @@ color: rgba(255, 255, 255, 0.6); } +.wifi-nearby-ap-speed { + font-weight: bold; + font-size: 0.85em; + margin-right: 8px; +} + .wifi-nearby-ap-signal { font-weight: bold; font-size: 0.85em;