Note on license: this add-on uses Psutil, licensed under 3-Clause BSD License which is compatible with GNU General Public License.
Version history:
+
Version 23.05.1
+
wlanReporter NVDA-addon is now part of resourceMonitor!
+
+
The old way of checking for wireless connections has been replaced by the windows API from wlanReporter: https://github.com/kvark128/WlanReporter/ .
+
After speaking SSID name and strength, NVDA will also now tell you the security type of your network.
+
NVDA will now alert you when you connect and disconnect from a wireless network.
+
NVDA will now alert you when wireless connections is turned on or off.
+
Version 23.05
added the ability to deteched and presents the state of the connected wireless network.
diff --git a/addon/doc/en/readme.md b/addon/doc/en/readme.md
index 755b690..0a61137 100644
--- a/addon/doc/en/readme.md
+++ b/addon/doc/en/readme.md
@@ -32,6 +32,13 @@ This add-on does not replace task manager and other system information programs
Note on license: this add-on uses Psutil, licensed under 3-Clause BSD License which is compatible with GNU General Public License.
# Version history:
+## Version 23.05.1
+wlanReporter NVDA-addon is now part of resourceMonitor!
+
+* The old way of checking for wireless connections has been replaced by the windows API from wlanReporter: https://github.com/kvark128/WlanReporter/ .
+ * After speaking SSID name and strength, NVDA will also now tell you the security type of your network.
+ * NVDA will now alert you when you connect and disconnect from a wireless network.
+ * NVDA will now alert you when wireless connections is turned on or off.
## Version 23.05
diff --git a/addon/globalPlugins/resourceMonitor/__init__.py b/addon/globalPlugins/resourceMonitor/__init__.py
index 69e8392..dc8ba68 100644
--- a/addon/globalPlugins/resourceMonitor/__init__.py
+++ b/addon/globalPlugins/resourceMonitor/__init__.py
@@ -4,24 +4,60 @@
# released under GPL.
# This add-on uses Psutil, licensed under 3-Clause BSD License which is compatible with GPL.
-import winreg
-import re
import functools
+import os.path
import platform
+import queueHandler
+import winreg
+import winsound
+from ctypes import addressof, byref, POINTER, wintypes
from datetime import datetime
-from subprocess import getoutput
from typing import List, Tuple, Union, Any
-import globalPluginHandler
-import ui
import api
+import globalCommands
+import globalPluginHandler
import scriptHandler
+import ui
import winVersion
from . import psutil
+from . import wlanapi
import addonHandler
addonHandler.initTranslation()
+MODULE_DIR = os.path.dirname(__file__)
+def message(text, fileName):
+ ui.message(text)
+ path = os.path.join(MODULE_DIR, fileName)
+ if os.path.exists(path):
+ winsound.PlaySound(path, winsound.SND_ASYNC)
+
+SECURITY_TYPE = {
+ wlanapi.DOT11_AUTH_ALGO_80211_OPEN: _("No authentication (Open)"),
+ wlanapi.DOT11_AUTH_ALGO_80211_SHARED_KEY: "WEP",
+ wlanapi.DOT11_AUTH_ALGO_WPA: "WPA-Enterprise",
+ wlanapi.DOT11_AUTH_ALGO_WPA_PSK: "WPA-PSK",
+ wlanapi.DOT11_AUTH_ALGO_RSNA: "WPA2-Enterprise",
+ wlanapi.DOT11_AUTH_ALGO_RSNA_PSK: "WPA2-PSK",
+}
+@wlanapi.WLAN_NOTIFICATION_CALLBACK
+def notifyHandler(pData, pCtx):
+ if pData.contents.NotificationSource != wlanapi.WLAN_NOTIFICATION_SOURCE_ACM:
+ return
+ if pData.contents.NotificationCode == wlanapi.wlan_notification_acm_connection_complete:
+ ssid = wlanapi.WLAN_CONNECTION_NOTIFICATION_DATA.from_address(pData.contents.pData).dot11Ssid.SSID
+ queueHandler.queueFunction(queueHandler.eventQueue, message, _("Connected to {}").format(ssid.decode("utf-8")), "connect.wav")
+ elif pData.contents.NotificationCode == wlanapi.wlan_notification_acm_disconnected:
+ ssid = wlanapi.WLAN_CONNECTION_NOTIFICATION_DATA.from_address(pData.contents.pData).dot11Ssid.SSID
+ queueHandler.queueFunction(queueHandler.eventQueue, message, _("Disconnected from {}").format(ssid.decode("utf-8")), "disconnect.wav")
+ elif pData.contents.NotificationCode == wlanapi.wlan_notification_acm_interface_arrival:
+ queueHandler.queueFunction(queueHandler.eventQueue, message, _("A wireless device has been enabled"), "enable.wav")
+ elif pData.contents.NotificationCode == wlanapi.wlan_notification_acm_interface_removal:
+ queueHandler.queueFunction(queueHandler.eventQueue, message, _("A wireless device has been disabled"), "disable.wav")
+
+def customResize(array, newSize):
+ return (array._type_ * newSize).from_address(addressof(array))
# Styles of size calculation/string composition, do not change!
-# Treditional style, Y, K, M, G, B, ...
+# Traditional style, Y, K, M, G, B, ...
traditional = [
(1024.0**8.0, 'Y'),
(1024.0**7.0, 'Z'),
@@ -232,10 +268,16 @@ def getWinVer() -> str:
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
-
# Translators: The gestures category for this add-on in input gestures dialog (2013.3 or later).
scriptCategory = _("Resource Monitor")
+ def __init__(self):
+ super().__init__()
+ self._negotiated_version = wintypes.DWORD()
+ self._client_handle = wintypes.HANDLE()
+ wlanapi.WlanOpenHandle(wlanapi.CLIENT_VERSION_WINDOWS_VISTA_OR_LATER, None, byref(self._negotiated_version), byref(self._client_handle))
+ wlanapi.WlanRegisterNotification(self._client_handle, wlanapi.WLAN_NOTIFICATION_SOURCE_ACM, True, notifyHandler, None, None, None)
+
@scriptHandler.script(
description=_(
# Translators: Input help message about battery info command in Resource Monitor.
@@ -359,35 +401,33 @@ def script_announceWinVer(self, gesture):
# Translators: Input help mode message about obtaining the ssid of the wireless network, and the strength of the network.
description=_("Announces the system's wireless network ssid name, and its strength."),
gesture="kb:NVDA+shift+8"
- )
- def script_network_information(self, gesture):
- # Get the SSID of the current network
- network_info = getoutput("netsh wlan show interface")
- ssid_pattern = re.compile(r'SSID\s+:\s(.+)')
- match = ssid_pattern.search(network_info)
- if match:
- ssid = match.group(1)
- else:
- # Translators: a message telling the user SSID not found.
- ui.message("SSID not found")
- return
-
- # Get the signal strength of the current network
- signal_strength = re.compile(r'Signal\s+:\s(\d+)')
- match = signal_strength.search(network_info)
+ )
+ def script_wlanStatusReport(self, gesture):
+ wlan_ifaces = POINTER(wlanapi.WLAN_INTERFACE_INFO_LIST)()
+ wlanapi.WlanEnumInterfaces(self._client_handle, None, byref(wlan_ifaces))
- if match:
- strength = int(match.group(1))
- else:
- # Translators: Message reported when there is no information on the strength of the ssid
- ui.message("Signal strength not found")
+ if wlan_ifaces.contents.NumberOfItems == 0:
+ info = _("No wireless devices")
+ wlanapi.WlanFreeMemory(wlan_ifaces)
return
+ for i in customResize(wlan_ifaces.contents.InterfaceInfo, wlan_ifaces.contents.NumberOfItems):
+ if i.isState != wlanapi.wlan_interface_state_connected:
+ info = _("No wireless connections")
+ continue
+
+ wlan_available_network_list = POINTER(wlanapi.WLAN_AVAILABLE_NETWORK_LIST)()
+ wlanapi.WlanGetAvailableNetworkList(self._client_handle, byref(i.InterfaceGuid), 0, None, byref(wlan_available_network_list))
+ for n in customResize(wlan_available_network_list.contents.Network, wlan_available_network_list.contents.NumberOfItems):
+ if n.Flags & wlanapi.WLAN_AVAILABLE_NETWORK_CONNECTED:
+ info = _("Connected wireless network: {}. Signal strength: {}%. Security type: {}").format(n.dot11Ssid.SSID.decode(), n.wlanSignalQuality, SECURITY_TYPE.get(n.dot11DefaultAuthAlgorithm))
+ break
+ wlanapi.WlanFreeMemory(wlan_available_network_list)
+ wlanapi.WlanFreeMemory(wlan_ifaces)
if scriptHandler.getLastScriptRepeatCount() == 0:
- # Translators: get information on the connected network information, and its strength.
- ui.message((f"Connected wireless network: {ssid}, Signal Strength: {strength}%"))
+ ui.message(info)
else:
- api.copyToClip((f"Connected wireless network: {ssid}, Signal Strength: {strength}%"), notify=True)
+ api.copyToClip(info, notify=True)
def getUptime(self) -> str:
bootTimestamp = psutil.boot_time()
diff --git a/addon/globalPlugins/resourceMonitor/connect.wav b/addon/globalPlugins/resourceMonitor/connect.wav
new file mode 100644
index 0000000..ea7cce7
Binary files /dev/null and b/addon/globalPlugins/resourceMonitor/connect.wav differ
diff --git a/addon/globalPlugins/resourceMonitor/disable.wav b/addon/globalPlugins/resourceMonitor/disable.wav
new file mode 100644
index 0000000..18bab54
Binary files /dev/null and b/addon/globalPlugins/resourceMonitor/disable.wav differ
diff --git a/addon/globalPlugins/resourceMonitor/disconnect.wav b/addon/globalPlugins/resourceMonitor/disconnect.wav
new file mode 100644
index 0000000..56fa00d
Binary files /dev/null and b/addon/globalPlugins/resourceMonitor/disconnect.wav differ
diff --git a/addon/globalPlugins/resourceMonitor/enable.wav b/addon/globalPlugins/resourceMonitor/enable.wav
new file mode 100644
index 0000000..c51a290
Binary files /dev/null and b/addon/globalPlugins/resourceMonitor/enable.wav differ
diff --git a/addon/globalPlugins/resourceMonitor/wlanapi.py b/addon/globalPlugins/resourceMonitor/wlanapi.py
new file mode 100644
index 0000000..2937545
--- /dev/null
+++ b/addon/globalPlugins/resourceMonitor/wlanapi.py
@@ -0,0 +1,149 @@
+from comtypes import GUID
+from ctypes import *
+from ctypes.wintypes import DWORD, PDWORD, HANDLE, BOOL
+
+wlanapi = windll.wlanapi
+
+WLAN_NOTIFICATION_SOURCE_ALL = 0x0000ffff
+WLAN_NOTIFICATION_SOURCE_ACM = 0x00000008
+wlan_notification_acm_connection_complete = 0x0000000a
+wlan_notification_acm_disconnected = 0x00000015
+wlan_notification_acm_interface_arrival = 0x0000000d
+wlan_notification_acm_interface_removal = 0x0000000e
+
+WLAN_AVAILABLE_NETWORK_CONNECTED = 1
+
+# State of the interface
+wlan_interface_state_connected = 1
+
+# Return codes
+ERROR_SUCCESS = 0
+
+# Client versions
+CLIENT_VERSION_WINDOWS_XP_SP3 = 1
+CLIENT_VERSION_WINDOWS_VISTA_OR_LATER = 2
+
+# Values of wireless LAN authentication algorithm
+DOT11_AUTH_ALGO_80211_OPEN = 1
+DOT11_AUTH_ALGO_80211_SHARED_KEY = 2
+DOT11_AUTH_ALGO_WPA = 3
+DOT11_AUTH_ALGO_WPA_PSK = 4
+DOT11_AUTH_ALGO_WPA_NONE = 5
+DOT11_AUTH_ALGO_RSNA = 6
+DOT11_AUTH_ALGO_RSNA_PSK = 7
+DOT11_AUTH_ALGO_IHV_START = 0x80000000
+DOT11_AUTH_ALGO_IHV_END = 0xffffffff
+
+class DOT11_SSID(Structure):
+ _fields_ = [
+ ("SSIDLength", c_ulong),
+ ("SSID", c_char * 32),
+ ]
+
+class WLAN_CONNECTION_NOTIFICATION_DATA(Structure):
+ _fields_ = [
+ ("wlanConnectionMode", c_uint),
+ ("strProfileName", c_wchar * 256),
+ ("dot11Ssid", DOT11_SSID),
+ ("dot11BssType", c_uint),
+ ("bSecurityEnabled", BOOL),
+ ("wlanReasonCode", DWORD),
+ ("dwFlags", DWORD),
+ ("strProfileXml", c_wchar * 1),
+ ]
+
+class WLAN_NOTIFICATION_DATA(Structure):
+ _fields_ = [
+ ("NotificationSource", DWORD),
+ ("NotificationCode", DWORD),
+ ("InterfaceGuid", GUID),
+ ("dwDataSize", DWORD),
+ ("pData", c_void_p),
+ ]
+
+class WLAN_AVAILABLE_NETWORK(Structure):
+ _fields_ = [
+ ("ProfileName", c_wchar * 256),
+ ("dot11Ssid", DOT11_SSID),
+ ("dot11BssType", c_uint),
+ ("NumberOfBssids", c_ulong),
+ ("NetworkConnectable", BOOL),
+ ("wlanNotConnectableReason", DWORD),
+ ("NumberOfPhyTypes", c_ulong),
+ ("dot11PhyTypes", c_uint * 8),
+ ("MorePhyTypes", BOOL),
+ ("wlanSignalQuality", c_ulong),
+ ("SecurityEnabled", BOOL),
+ ("dot11DefaultAuthAlgorithm", c_uint),
+ ("dot11DefaultCipherAlgorithm", c_uint),
+ ("Flags", DWORD),
+ ("Reserved", DWORD),
+ ]
+
+class WLAN_AVAILABLE_NETWORK_LIST(Structure):
+ _fields_ = [
+ ("NumberOfItems", DWORD),
+ ("Index", DWORD),
+ ("Network", WLAN_AVAILABLE_NETWORK * 1),
+ ]
+
+class WLAN_INTERFACE_INFO(Structure):
+ _fields_ = [
+ ("InterfaceGuid", GUID),
+ ("strInterfaceDescription", c_wchar * 256),
+ ("isState", c_uint),
+ ]
+
+class WLAN_INTERFACE_INFO_LIST(Structure):
+ _fields_ = [
+ ("NumberOfItems", DWORD),
+ ("Index", DWORD),
+ ("InterfaceInfo", WLAN_INTERFACE_INFO * 1),
+ ]
+
+WLAN_NOTIFICATION_CALLBACK = CFUNCTYPE(None, POINTER(WLAN_NOTIFICATION_DATA), POINTER(c_void_p))
+
+def errcheck(result, func, args):
+ if result != ERROR_SUCCESS:
+ raise WinError(c_long(result).value)
+ return result
+
+def WlanOpenHandle(dwClientVersion, pReserved, pdwNegotiatedVersion, phClientHandle):
+ """ The WlanOpenHandle function opens a connection to the server. """
+ wlanapi.WlanOpenHandle.errcheck = errcheck
+ wlanapi.WlanOpenHandle.argtypes = [DWORD, c_void_p, POINTER(DWORD), POINTER(HANDLE)]
+ wlanapi.WlanOpenHandle.restype = DWORD
+ return wlanapi.WlanOpenHandle(dwClientVersion, pReserved, pdwNegotiatedVersion, phClientHandle)
+
+def WlanEnumInterfaces(hClientHandle, pReserved, ppInterfaceList):
+ """ The WlanEnumInterfaces function enumerates all of the wireless LAN interfaces currently enabled on the local computer. """
+ wlanapi.WlanEnumInterfaces.errcheck = errcheck
+ wlanapi.WlanEnumInterfaces.argtypes = [HANDLE, c_void_p, POINTER(POINTER(WLAN_INTERFACE_INFO_LIST))]
+ wlanapi.WlanEnumInterfaces.restype = DWORD
+ return wlanapi.WlanEnumInterfaces(hClientHandle, pReserved, ppInterfaceList)
+
+def WlanGetAvailableNetworkList(hClientHandle, pInterfaceGuid, dwFlags, pReserved, ppAvailableNetworkList):
+ """ The WlanGetAvailableNetworkList function retrieves the list of available networks on a wireless LAN interface. """
+ wlanapi.WlanGetAvailableNetworkList.errcheck = errcheck
+ wlanapi.WlanGetAvailableNetworkList.argtypes = [HANDLE, POINTER(GUID), DWORD, c_void_p, POINTER(POINTER(WLAN_AVAILABLE_NETWORK_LIST))]
+ wlanapi.WlanGetAvailableNetworkList.restype = DWORD
+ return wlanapi.WlanGetAvailableNetworkList(hClientHandle, pInterfaceGuid, dwFlags, pReserved, ppAvailableNetworkList)
+
+def WlanFreeMemory(pMemory):
+ """ The WlanFreeMemory function frees memory. Any memory returned from Native Wifi functions must be freed. """
+ wlanapi.WlanFreeMemory.argtypes = [c_void_p]
+ wlanapi.WlanFreeMemory(pMemory)
+
+def WlanCloseHandle(hClientHandle, pReserved):
+ """ The WlanCloseHandle function closes a connection to the server. """
+ wlanapi.WlanCloseHandle.errcheck = errcheck
+ wlanapi.WlanCloseHandle.argtypes = [HANDLE, c_void_p]
+ wlanapi.WlanCloseHandle.restype = DWORD
+ return wlanapi.WlanCloseHandle(hClientHandle, pReserved)
+
+def WlanRegisterNotification(hClientHandle, dwNotifSource, bIgnoreDuplicate, funcCallback, pCallbackContext, pReserved, pdwPrevNotifSource):
+ """ The WlanRegisterNotification function is used to register and unregister notifications on all wireless interfaces. """
+ wlanapi.WlanRegisterNotification.errcheck = errcheck
+ wlanapi.WlanRegisterNotification.argtypes = [HANDLE, DWORD, BOOL, WLAN_NOTIFICATION_CALLBACK, c_void_p, c_void_p, PDWORD]
+ wlanapi.WlanRegisterNotification.restype = DWORD
+ return wlanapi.WlanRegisterNotification(hClientHandle, dwNotifSource, bIgnoreDuplicate, funcCallback, pCallbackContext, pReserved, pdwPrevNotifSource)
diff --git a/buildVars.py b/buildVars.py
index 52708f2..d3ad0b6 100644
--- a/buildVars.py
+++ b/buildVars.py
@@ -25,13 +25,13 @@ def _(arg):
# Translators: Long description to be shown for this add-on on add-on information from add-ons manager
"addon_description": _("A handy resource monitor to report CPU load, memory usage, battery, disk usage status and more."),
# version
- "addon_version": "23.05",
+ "addon_version": "23.05.1",
# Author(s)
"addon_author": "Alex Hall , Joseph Lee , Kefas Lungu , beqa gozalishvili , Tuukka Ojala , Ethin Probst and other NVDA contributors",
# URL for the add-on documentation support
"addon_url": "https://addons.nvda-project.org/",
# URL for the add-on repository where the source code can be found
- "addon_sourceURL": "https://github.com/kefaslungu/resourcemonitor",
+ "addon_sourceURL": "https://github.com/kefaslungu/resourceMonitor",
# Documentation file name
"addon_docFileName": "readme.html",
# Minimum NVDA version supported (e.g. "2018.3.0", minor version is optional)
diff --git a/readme.md b/readme.md
index 755b690..0a61137 100644
--- a/readme.md
+++ b/readme.md
@@ -32,6 +32,13 @@ This add-on does not replace task manager and other system information programs
Note on license: this add-on uses Psutil, licensed under 3-Clause BSD License which is compatible with GNU General Public License.
# Version history:
+## Version 23.05.1
+wlanReporter NVDA-addon is now part of resourceMonitor!
+
+* The old way of checking for wireless connections has been replaced by the windows API from wlanReporter: https://github.com/kvark128/WlanReporter/ .
+ * After speaking SSID name and strength, NVDA will also now tell you the security type of your network.
+ * NVDA will now alert you when you connect and disconnect from a wireless network.
+ * NVDA will now alert you when wireless connections is turned on or off.
## Version 23.05