WifiConnectionAPI.psm1
|
<#
.Synopsis Wifi .Description Manage Wifi connections .Parameter Get-RadioState .Parameter Set-RadioState .Parameter Get-WifiDeviceInformation .Parameter Get-WiFiAvailableAdapter .Parameter Get-WifiConnectionProfile .Parameter Remove-WifiConnectionProfile .Parameter Get-WifiCurrentConection .Parameter Get-WifiAvailableNetwork .Parameter Search-WifiNetworks .Parameter Disconnect-WifiNetwork .Parameter Connect-WiFiNetwork #> ImportSystemModules Add-Type -AssemblyName System.Runtime.WindowsRuntime # Zoek de juiste overloads voor zowel Operations (met resultaat) als Actions (zonder resultaat) $asTaskGeneric = [System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' #-and $_.GetParameters().Count -eq 1 } $asTaskOperation = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' } $asTaskAction = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncAction' } $asTaskOperationCancel = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 2 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' -and $_.GetParameters()[1].ParameterType.Name -eq 'CancellationToken' } $asTaskActionCancel = $asTaskGeneric | Where-Object { $_.GetParameters().Count -eq 2 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncAction' -and $_.GetParameters()[1].ParameterType.Name -eq 'CancellationToken' } Function Await($WinRtTask, $ResultType, [int]$TimeoutMs = 10000, $ProgressBarName) { process { # Forceer de timeout tussen 1000 en 60000 ms if ($TimeoutMs -lt 1000) { $TimeoutMs = 1000 } if ($TimeoutMs -gt 60000) { $TimeoutMs = 60000 } if ($ResultType) { $asTask = $asTaskOperation.MakeGenericMethod($ResultType) $netTask = $asTask.Invoke($null, @($WinRtTask)) } else { $netTask = $asTaskAction.Invoke($null, @($WinRtTask)) } if ($ProgressBarName) { $elapsed = 0 $interval = 500 while (-not $netTask.IsCompleted -and $elapsed -lt $TimeoutMs) { # Bereken percentage op basis van de voortgang van de TIMEOUT $percent = [int](($elapsed / $TimeoutMs) * 100) Write-Progress -Activity $ProgressBarName ` -Status "Wachten op respons... ($($elapsed)ms / $($TimeoutMs)ms)" ` -PercentComplete $percent Start-Sleep -Milliseconds $interval $elapsed += $interval } } else { # Zonder naam blokkeren we gewoon voor de ingestelde tijd [Void]$netTask.Wait($TimeoutMs) } # Resultaat afhandeling if ($netTask.IsCompleted) { if ($ResultType) { return $netTask.Result } return $true } else { Write-Error "De WinRT terminated after $($TimeoutMs)ms." return $null } } } Function AwaitWithCancel { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $WinRtTask, [type] $ResultType, [int] $TimeoutMs = 10000, [string] $ProgressBarName # De naam voor je voortgangsbalk ) $cts = New-Object System.Threading.CancellationTokenSource $timer = [System.Diagnostics.Stopwatch]::StartNew() [System.GC]::KeepAlive($cts) try { # Selecteer de juiste overload (met Cancel) if ($ResultType) { $method = $asTaskOperationCancel.MakeGenericMethod($ResultType) } else { $method = $asTaskActionCancel } # Start de taak met de Token $netTask = $method.Invoke($null, @($WinRtTask, $cts.Token)) # De verbeterde Polling Loop met Progress Bar support while (-not $netTask.IsCompleted -and $timer.ElapsedMilliseconds -lt $TimeoutMs) { if ($ProgressBarName) { # Bereken voortgang op basis van de verstreken tijd t.o.v. de timeout $elapsed = $timer.ElapsedMilliseconds $percent = [Math]::Min(100, [int](($elapsed / $TimeoutMs) * 100)) Write-Progress -Activity $ProgressBarName ` -Status "Wachten op WinRT respons... ($($elapsed)ms / $($TimeoutMs)ms)" ` -PercentComplete $percent } Start-Sleep -Milliseconds 200 } if ($netTask.IsCompleted) { if ($ResultType) { return $netTask.Result } else { return $true } } else { Write-Warning "Timeout: Operatie afgebroken." $cts.Cancel() return $null } } catch { # In PS 5.1 wordt dit vaak overgeslagen bij Ctrl+C! if ($null -ne $cts) { $cts.Cancel() } } finally { # DIT BLOK DRAAIT BIJNA ALTIJD, OOK BIJ CTRL+C # Check of we gestopt zijn VOORDAT de taak klaar was (User Interrupt) if ($null -ne $netTask -and -not $netTask.IsCompleted -and $timer.ElapsedMilliseconds -lt $TimeoutMs) { #[Console]::ForegroundColor = "Yellow" #[Console]::WriteLine("`r`n[!] ONDERBROKEN: De WinRT-hardware is gestopt.") #[Console]::ResetColor() if ($null -ne $cts) { $cts.Cancel() } } $timer.Stop() $cts.Dispose() [System.GC]::Collect() [System.GC]::WaitForPendingFinalizers() } } [Windows.Devices.Radios.Radio, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null [Windows.Devices.Geolocation.Geolocator, Windows.Devices.Geolocation, ContentType = WindowsRuntime] | Out-Null function Request-LocationPermission { [outputType([Windows.Devices.Geolocation.GeolocationAccessStatus])] param( [Switch]$Sillent ) $Type = ([Windows.Devices.Geolocation.GeolocationAccessStatus]) $AsyncOp = ([Windows.Devices.Geolocation.Geolocator]::RequestAccessAsync()) $Result = Await -WinRtTask $AsyncOp -ResultType $Type # 1. Al toegang? Dan direct klaar. if ($Result -eq [Windows.Devices.Geolocation.GeolocationAccessStatus]::Allowed -or $Sillent) { return $Result } # 2. Geen toegang: Vraag de gebruiker Write-Host "`n[!] Locatie-access needed for this module." -ForegroundColor Yellow $msg = "PowerShell needs locatie-access for WiFi-functions.`n`nwould you like to open private settings now?" $shell = New-Object -ComObject WScript.Shell # 4 = Yes/No knoppen, 32 = Vraagteken icoon, 10 = Seconden timeout $response = $shell.Popup($msg, 10, "Locatie-toegang Vereist", 4 + 32) #$choice = Read-Host "Wil je de Windows-instellingen openen om dit aan te passen? (y/n)" if ($response -ne 6) { # GEBRUIKER ZEGT NEE -> Stop de functie hier Write-Warning "User has access denied. function terminated." return $Result # Geeft 'Denied' terug aan de aanroeper } # 3. Gebruiker zegt JA -> Open instellingen en start de loop Start-Process "ms-settings:privacy-location" #AllowDesktopApps" Write-Host "`n[!] ACTIE VEREIST IN HET GEOPENDE VENSTER:" -ForegroundColor Yellow Write-Host ">>> Scroll Down And Allow Desktop-Apps access to the Location <<<" -ForegroundColor Cyan -BackgroundColor Black Write-Host ">>> Check that the switch on top is also switched on<<<`n" -ForegroundColor Red Write-Host "Waiting for Acces (press CTRL+C to exit)..." -NoNewline $MaxTries = 30 # Stop na 30 seconden als er niets gebeurt (timeout) while ($Result -ne [Windows.Devices.Geolocation.GeolocationAccessStatus]::Allowed -and $MaxTries -gt 0) { Write-Host "." -NoNewline Start-Sleep -Seconds 1 $AsyncOp = ([Windows.Devices.Geolocation.Geolocator]::RequestAccessAsync()) $Result = AwaitWithCancel -WinRtTask $AsyncOp -ResultType $Type $MaxTries-- } if ($Result -ne [Windows.Devices.Geolocation.GeolocationAccessStatus]::Allowed) { Write-Error "`nTime-out: access blocked" } else { Write-Host "`naccess allowed! lets go on ..." -ForegroundColor Green } return $Result } [Windows.Devices.Radios.RadioState, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null [Windows.Devices.Radios.RadioAccessStatus, Windows.System.Devices, ContentType = WindowsRuntime] | Out-Null Function Get-RadioState { <# .SYNOPSIS Retrieves all available radio adapters (WiFi, Bluetooth, Mobile) and displays their current status. .DESCRIPTION This function uses the Windows Runtime (WinRT) API to access the system's radio hardware directly. The result is a list of Radio objects that can be piped directly into Set-RadioState. .EXAMPLE Get-RadioState Displays all radios and whether they are 'On' or 'Off'. .EXAMPLE Get-RadioState | Where-Object Kind -eq 'WiFi' | Set-RadioState -SetState Off Specifically searches for the WiFi radio and turns it off. .OUTPUTS Windows.Devices.Radios.Radio #> [outputType([Windows.Devices.Radios.Radio])] param( [int]$TimeoutMS = 10000 ) Process { $accessStatus = Await ([Windows.Devices.Radios.Radio]::RequestAccessAsync()) ([Windows.Devices.Radios.RadioAccessStatus]) if ($accessStatus -eq "Allowed") { Await -WinRtTask ([Windows.Devices.Radios.Radio]::GetRadiosAsync()) -ResultType ([System.Collections.Generic.IReadOnlyList[Windows.Devices.Radios.Radio]]) -TimeoutMs $TimeoutMS } else { Write-Warning "No Access to radio hardware Status: $accessStatus" } } } Function Set-RadioState { <# .SYNOPSIS Turns a specific radio on or off. .DESCRIPTION Accepts Radio objects (typically from the pipeline of Get-RadioState) and attempts to change the physical status to 'On' or 'Off'. .PARAMETER SetState The desired target state. Use 'On' or 'Off'. (Supports autocomplete). .PARAMETER Radios The Radio object to be modified. Accepts input via the pipeline. .EXAMPLE Get-RadioState | Set-RadioState -SetState Off Get-RadioState | Where-Object Name -eq 'Wi-Fi' | Set-RadioState -SetState On Attempts to turn off all radios on the system. .NOTES Some adapters (such as Mobile Broadband) may refuse the status change if they are managed elsewhere by the system. In such cases, the function will issue a warning rather than crashing. #> param( [Parameter(Mandatory = $true)]$SetState, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][Alias("Radio")][Windows.Devices.Radios.Radio[]]$Radios, [int]$TimeoutMs = 10000 ) process { foreach ($R in $Radios) { $Task = $R.SetStateAsync($SetState) $ResultType = [Windows.Devices.Radios.RadioAccessStatus] $Status = Await -WinRtTask $Task -ResultType $ResultType -TimeoutMs $TimeoutMs if ($Status -ne 'Allowed') { Write-Warning "Radio $($R.Name) access status: $Status" } } } } [Windows.Devices.WiFi.WiFiAdapter, Windows.Devices.Wifi, ContentType = WindowsRunTime] | Out-Null [Windows.Devices.WiFi.WiFiReconnectionKind, Windows.DEvices.Wifi, ContentType = WindowsRuntime] | Out-Null [Windows.Devices.Enumeration.DeviceInformation, Windows.Devices.Enumeration, ContentType = WindowsRunTime] | Out-Null [Windows.Security.Credentials.PasswordCredential, Windows.Security.Credentials, ContentType = WindowsRunTime] | Out-Null [Windows.Networking.Connectivity.NetworkInformation, Windows.Networking.Connectivity, Contenttype = WindowsRuntime] | Out-Null Function Get-WifiDeviceInformation { [OutputType([Windows.Devices.Enumeration.DeviceInformationCollection])] Param( [int]$TimeoutMs = 10000 ) Process { Await -WinRtTask ([Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync([Windows.Devices.WiFi.WiFiAdapter]::GetDeviceSelector())) -ResultType ([Windows.Devices.Enumeration.DeviceInformationCollection]) -TimeoutMs $TimeoutMs } } Function Get-WiFiAvailableAdapter { [outputtype([Windows.Devices.WiFi.WiFiAdapter])] param( [int]$TimeoutMs = 10000 ) Process { if ( (Request-LocationPermission) -ne [Windows.Devices.Geolocation.GeolocationAccessStatus]::Allowed) { Write-Warning "WiFi-informatie can not be retrieved without locatie-Access."; return } Await -WinRtTask ([Windows.Devices.WiFi.WiFiAdapter]::FindAllAdaptersAsync()) -ResultType ([System.Collections.Generic.IReadOnlyList[Windows.Devices.WiFi.WiFiAdapter]]) -TimeoutMs $TimeoutMs } } Function Search-WifiNetworks { [OutputType([Windows.Devices.Wifi.WifiAvailableNetwork])] param( # We laten het type vrij of gebruiken de WinRT klasse [Parameter(ValueFromPipeline = $true)] [Windows.Devices.WiFi.WiFiAdapter]$WiFiAdapter, [int]$TimeoutMs = 10000 ) begin { # De 'Smart Load' logica if (-not $WiFiAdapter) { # Gebruik je bestaande stabiele functie als fallback $WiFiAdapter = Get-WiFiAvailableAdapter -TimeoutMs $TimeoutMs } } Process { if (-not $WiFiAdapter) { Return } Write-Progress -Activity "WiFi" -Status "Scanning WiFi Networks" [Void](AwaitWithCancel -WinRtTask ($WiFiAdapter.ScanAsync()) -ResultType ($null) -TimeoutMs $TimeoutMs -ProgressBarName "WiFi" ) Write-Progress -Activity "WiFi" -Completed $script:WiFiLastScanTime = Get-Date return $WifiAdapter.NetworkReport.AvAilableNetworks } } Function Get-WifiConnectionProfile { [OutputType([Windows.Networking.Connectivity.ConnectionProfile[]])] Param() Process { try { $Profiles = [Windows.Networking.Connectivity.NetworkInformation]::GetConnectionProfiles() $WifiProfiles = $Profiles | Where-Object { $_.IsWlanConnectionProfile } if ($null -eq $WifiProfiles) { Write-Verbose "No WiFi connection profiles found on this system." } return $WifiProfiles } catch { Write-Error "Failed to retrieve connection profiles: $($_.Exception.Message)" } } } Function Remove-WifiConnectionProfile { param( [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("Profile")] [Windows.Networking.Connectivity.ConnectionProfile[]]$Profiles, [int]$TimeoutMS = 10000 # Nu netjes als laatste parameter ) process { foreach ($P in $Profiles) { if ($P.CanDelete) { Write-Host ("Removing Profile {0}" -f $P.ProfileName) -ForegroundColor Cyan # De WinRT call klaarzetten $task = $P.TryDeleteAsync() $resultType = [Windows.Networking.Connectivity.ConnectionProfileDeleteStatus] # Await aanroepen met de gekozen timeout $status = Await -WinRtTask $task -ResultType $resultType -TimeoutMs $TimeoutMS if ($status -eq "Success") { Write-Host "Profile Succesfull removed" -ForegroundColor Green } elseif ($null -eq $status) { Write-Warning "Timeout reached for removing profile: $($P.ProfileName)." } else { Write-Error "Error drunig remove: $($status)" } } else { Write-Warning ("Cant Remove Profile {0}" -f $P.ProfileName) } } } } Function Get-WifiCurrentConnection { [OutputType([Windows.Networking.Connectivity.ConnectionProfile[]])] Param( [Parameter(Position = 0)] [int[]]$Index, [Parameter(Position = 1)] [int]$TimeoutMS = 10000 ) Process { try { $Adapters = Get-WiFiAvailableAdapter if ($null -eq $Adapters -or $Adapters.Count -eq 0) { Write-Warning "No WiFi adapters found to check connections." return $null } # Determine which adapters to target based on Index $Targets = if ($PSBoundParameters.ContainsKey('Index')) { foreach ($i in $Index) { if ($i -ge 0 -and $i -lt $Adapters.Count) { $Adapters[$i] } else { Write-Warning "Index $i skipped: does not exist (range: 0 to $($Adapters.Count - 1))." } } } else { $Adapters # Default: Check all } $Results = [System.Collections.Generic.List[Object]]::new() foreach ($Adapter in $Targets) { $Task = $Adapter.NetworkAdapter.GetConnectedProfileAsync() $ResultType = [Windows.Networking.Connectivity.ConnectionProfile] # Await the async task $CurrentWifiProfile = Await -WinRtTask $Task -ResultType $ResultType -TimeoutMs $TimeoutMS if ($null -ne $CurrentWifiProfile) { $Results.Add($CurrentWifiProfile) } } if ($Results.Count -eq 0) { Write-Verbose "No active WiFi connection found on the selected adapter(s)." return $null } return $Results.ToArray() } catch { Write-Error "Failed to retrieve current connection: $($_.Exception.Message)" return $null } } } Function Disconnect-WifiNetwork { param( [Parameter(Mandatory = $false, Position = 0)] [int[]]$Index ) Process { $Adapters = Get-WiFiAvailableAdapter if ($null -eq $Adapters -or $Adapters.Count -eq 0) { Write-Warning "No WiFi adapters found to disconnect." return } $Targets = if ($PSBoundParameters.ContainsKey('Index')) { foreach ($i in $Index) { if ($i -ge 0 -and $i -lt $Adapters.Count) { $Adapters[$i] } else { Write-Warning "Index $i skipped: does not exist (range: 0 to $($Adapters.Count - 1))." } } } else { $Adapters } foreach ($A in $Targets) { try { $A.Disconnect() Write-Host "Disconnected adapter: $($A.NetworkAdapter.NetworkAdapterId)" -ForegroundColor Green } catch { Write-Error "Failed to disconnect: $($_.Exception.Message)" } } } } Function Get-WifiAvailableNetworks { [CmdletBinding()] # CRUCIAAL: Dit activeert de Write-Debug/Verbose functionaliteit [OutputType([Windows.Devices.Wifi.WifiAvailableNetwork])] param( [String]$DeviceName ) Process { if ($PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = [System.Management.Automation.ActionPreference]::Continue } #$DeviceName = $PSBoundParameters['DeviceName'] # Zoek de adapter op basis van de InterfaceDescription (de naam uit de completer) if (-not [string]::IsNullOrEmpty(($DeviceName)) ) { $adapter = Get-NetAdapter | Where-Object { $_.InterfaceDescription -eq $DeviceName } if ([string]::IsNullOrEmpty(($adapter)) ) { Write-Error "Adapter '$DeviceName' niet gevonden." return # Stop de uitvoering voor dit item } else { # Haal de GUID (InstanceID) op $adapterGuid = $adapter.InstanceID.Trim('{').Trim('}') Write-Debug "GUID gevonden voor $DeviceName : $adapterGuid" } } if ($PSBoundParameters.ContainsKey('Debug')) { $DebugPreference = [System.Management.Automation.ActionPreference]::Continue } Write-Debug "GUID:'$adapterGuid'" Write-Debug "devicename:'$DeviceName'" if ([string]::IsNullOrEmpty($DeviceName)) { Write-Debug "No Device Selected return all" return (Get-WiFiAvailableAdapter).NetworkReport.AvailableNetworks } elseif (-not [string]::IsNullOrEmpty($adapterGuid)) { write-debug "A Device has been Selected return specifick" return (Get-WiFiAvailableAdapter | Where-Object { $_.NetworkAdapter.NetworkAdapterID -match $adapterGuid }).NetworkReport.AvailableNetworks } else { Write-Debug "The Device Name was wrong fix it" } } } Function Connect-WiFiNetwork { <# .SYNOPSIS Connects one, multiple, or all WiFi adapters to a specific network (SSID). .DESCRIPTION This function uses the WinRT API to connect. It supports simultaneously targeting specific adapters by their index. If a network is already known to Windows (profile exists), the password is not required. .PARAMETER Ssid Mandatory The name of the WiFi network. Use the TAB key for suggestions of available nearby networks. .PARAMETER Password Optional The password for the network. This is a dynamic parameter that uses SecureString (unless -AsPlainText is specified). Not required if a stored profile already exists. .PARAMETER Index Optional The index of the adapter(s) (e.g., 0, 1). Leave this blank to attempt the connection on ALL available WiFi adapters simultaneously. .PARAMETER User Optional username for enterprise networks (802.1X). .PARAMETER Bssid Optional Force connection to a specific Access Point based on the MAC address. .EXAMPLE Connect-WiFiNetwork -Ssid "MyHomeNetwork" -Index 0 Connects the first adapter to the specified network. .EXAMPLE Connect-WiFiNetwork -Ssid "Open_WiFi" Attempts to connect all WiFi adapters on the system to an open network. .EXAMPLE Connect-WiFiNetwork -Ssid "Company_WiFi" -Password (Read-Host -AsSecureString) -Index 0, 2 Connects adapter 0 and 2 to a secured network with manual password input. .NOTES The function checks each adapter to ensure the SSID is visible before initiating the connection. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)]$Ssid, [string]$Bssid, [string]$User, [Switch]$AsPlainText, [Switch]$Hidden, [int[]]$Index, [Int]$TimeoutMS = 25000, [Windows.Devices.WiFi.WiFiReconnectionKind]$WifiReconnectionKind = [Windows.Devices.WiFi.WiFiReconnectionKind]::Automatic ) DynamicParam { # Kies het type op basis van de switch $parameterType = if ($AsPlainText) { [string] } else { [securestring] } # Maak de collectie voor attributen (zoals Mandatory) $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $attribute = New-Object System.Management.Automation.ParameterAttribute $attribute.Mandatory = $false $attributeCollection.Add($attribute) # Definieer de parameter 'Password' runtime $dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Password', $parameterType, $attributeCollection) # Voeg toe aan de dictionary die PowerShell terug verwacht $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $paramDictionary.Add('Password', $dynParam) return $paramDictionary } #Begin { <#Process { if ( (Request-LocationPermission) -ne [Windows.Devices.Geolocation.GeolocationAccessStatus]::Allowed) { Write-Warning "WiFi-informatie can not be retrieved without locatie-Access."; return } # Wachtwoord-check (Zoals we eerder bespraken: PlainText vs SecureString) if ($Password -and $Password -isnot [System.Security.SecureString] -and -not $AsPlainText) { throw [System.Security.SecurityException]::new("Password is plainText. Use the switch -AsPlainText or use SecureString in.") } # 1. Directe validatie van parameters if ($Hidden -and -not $Bssid) { throw "Error: when -Hidden is used -Bssid is Mandatory" } # 2. Haal de adapter op (gebruik je geinitialiseerde Await uit de module-scope) $WiFiAvailableAdapter = Get-WiFiAvailableAdapter #| Select-Object -First 1 if (-not $WiFiAvailableAdapter) { throw "No actieve WiFi-adapter found." } $AllWifiRadios = Get-RadioState | Where-Object { $_.Kind -eq 'WiFi' } $OnWifiRadios = $AllWifiRadios | Where-Object { $_.Kind -eq 'WiFi' -and $_.State -eq 'On' } if ($AllWifiRadios.Count -gt 0 -and $null -eq $OnWifiRadios) { # Hard stop: Nothing is working throw "Error: All WiFi radios are OFF. Use 'Get-RadioState | Set-RadioState -SetState On' first." } elseif ($OnWifiRadios.Count -lt $AllWifiRadios.Count) { # Soft warning: Some are off, but we can still try with the ones that are on Write-Warning "Some WiFi radios are OFF. If your adapter index is not responding, check its radio state." } # 3. De 'Smart Find' Logica # We proberen eerst de cache $Network = $WiFiAvailableAdapter.NetworkReport.AvailableNetworks | Where-Object { if ($Bssid) { $_.Bssid -eq $Bssid } else { $_.Ssid -eq $Ssid } } # 4. Nood-scan als het netwerk niet in de cache zit en het GEEN hidden netwerk is if (-not $Network -and -not $Hidden) { #Write-Host "SSID '$Ssid' niet gevonden in cache, start eenmalige scan..." -ForegroundColor Yellow $null = Search-WifiNetworks # Probeer het nog één keer na de scan $Network = $WiFiAvailableAdapter.NetworkReport.AvailableNetworks | Where-Object { if ($Bssid) { $_.Bssid -eq $Bssid } else { $_.Ssid -eq $Ssid } } | Select-Object -First 1 } # 5. De genadeslag: als we hem nu nog niet hebben, stoppen we direct if (-not $Network) { if ($Hidden) { throw "Error: Can't find hidden network with BSSID '$Bssid'" } else { throw "Error: SSID '$Ssid' not found even afther scan" } } <#} Process { #> <# $PasswordValue = $PSBoundParameters['Password'] $SsidValue = $PSBoundParameters['Ssid'] # 1. Haal de adapters op $WiFiAvailableAdapter = Get-WiFiAvailableAdapter # 2. Bepaal op welke adapters we actie ondernemen $TargetAdapters = if ($PSBoundParameters.ContainsKey('Index')) { foreach ($i in $Index) { $WiFiAvailableAdapter[$i] } } else { $WiFiAvailableAdapter # Standaard: Probeer op ALLE adapters te verbinden } $Credentials = [Windows.Security.Credentials.PasswordCredential]::new() if ($null -ne $passwordValue) { if ($AsPlainText) { $Credentials.Password = $passwordValue } else { $Credentials.Password = ([System.Net.NetworkCredential]::new([string]::empty, $passwordValue)).Password } } if ([bool]$User) { $Credentials.UserName = $User } Write-Progress -Activity "Connecting" -status "Connecting Wifi Network" $TargetAdapters | Foreach-Object { # Haal de menselijke naam op voor de huidige adapter in de loop $currentId = $_.NetworkAdapter.NetworkAdapterId $adapterName = (Get-NetAdapter | Where-Object DeviceID -match $currentId).InterfaceDescription Write-Host "Connect over adapter: $adapterName" -ForegroundColor Cyan if (-not $Bssid) { $Network = $_.NetworkReport.AvailableNetworks | Where-Object { $_.Ssid -match $SsidValue } | Sort-Object NetworkRssiInDecibelMilliwatts -Descending | Select-Object -First 1 } else { $Network = $_.NetworkReport.AvailableNetworks | Where-Object { $_.BSsid -match $BSsid } } $_.Disconnect() $Result = AwaitWithCancel -WinRtTask ($_.ConnectAsync($Network, $WifiReconnectionKind, $Credentials, $null)) -ResultType ([Windows.Devices.WiFi.WiFiConnectionResult]) -TimeoutMs $TimeoutMS -ProgressBarName 'Connecting' # Optioneel: Laat direct weten of het gelukt is voor deze specifieke kaart $statusColor = "Yellow" if ($Result.ConnectionStatus -eq "Success") { $statusColor = "Green" } Write-Host "Status for $adapterName : $($Result.ConnectionStatus)" -ForegroundColor $statusColor [PSCustomObject]@{ ConnectionStatus = $Result.ConnectionStatus # NIET hernoemen! AdapterName = $adapterName Ssid = $SsidValue Index = [array]::IndexOf($WiFiAvailableAdapter, $_) Bssid = $Network.Bssid } } Write-Progress -Activity "Connecting" -Completed }#> Process { # --- 1. PERMISSIE & RADIO CHECK --- if ((Request-LocationPermission) -ne 'Allowed') { return } $Adapters = Get-WiFiAvailableAdapter if (-not $Adapters) { throw "No active WiFi-adapter found." } # Radio check (Simpeler geschreven) $Radios = Get-RadioState | Where-Object Kind -eq 'WiFi' if ($Radios.Count -gt 0 -and -not ($Radios | Where-Object State -eq 'On')) { throw "Error: All WiFi radios are OFF. Turn them on first." } # --- 2. CREDENTIALS VOORBEREIDEN --- $Pass = $PSBoundParameters['Password'] $Creds = [Windows.Security.Credentials.PasswordCredential]::new() if ($null -ne $Pass) { if ($AsPlainText) { $Creds.Password = $Pass } else { $Creds.Password = ([System.Net.NetworkCredential]::new("", $Pass)).Password } } if ($User) { $Creds.UserName = $User } # --- 3. TARGET ADAPTERS BEPALEN --- $Targets = if ($PSBoundParameters.ContainsKey('Index')) { $Index | ForEach-Object { $Adapters[$_] } } else { $Adapters } # --- 4. CONNECTIE LOOP --- $Targets | ForEach-Object { $currentId = $_.NetworkAdapter.NetworkAdapterId $name = (Get-NetAdapter | Where-Object DeviceID -match $currentId).InterfaceDescription # Zoek netwerk specifiek voor DEZE adapter $Net = $_.NetworkReport.AvailableNetworks | Where-Object { if ($Bssid) { $_.Bssid -eq $Bssid } else { $_.Ssid -eq $Ssid } } | Sort-Object NetworkRssiInDecibelMilliwatts -Descending | Select-Object -First 1 if (-not $Net -and -not $Hidden) { # Forceer scan indien niet gevonden $null = Search-WifiNetworks # Ik neem aan dat dit een interne helper is $Net = $_.NetworkReport.AvailableNetworks | Where-Object { if ($Bssid) { $_.Bssid -eq $Bssid } else { $_.Ssid -eq $Ssid } } | Select-Object -First 1 } if (-not $Net) { Write-Warning "Adapter $($name): SSID '$($Ssid)' not found."; return } Write-Host "Connecting $name to $Ssid..." -ForegroundColor Cyan $_.Disconnect() $Task = $_.ConnectAsync($Net, $WifiReconnectionKind, $Creds, $null) $Res = AwaitWithCancel -WinRtTask $Task -ResultType ([Windows.Devices.WiFi.WiFiConnectionResult]) -TimeoutMs $TimeoutMS -ProgressBarName "Connecting $Ssid" # Output [PSCustomObject]@{ ConnectionStatus = $Res.ConnectionStatus AdapterName = $name Ssid = $Ssid Bssid = $Net.Bssid } } } } function Get-WifiBandName { param ([long]$ChannelCenterFrequencyInKilohertz) switch ($ChannelCenterFrequencyInKilohertz) { { $_ -ge 2400000 -and $_ -le 2500000 } { "2.4 GHz"; break } { $_ -ge 5150000 -and $_ -le 5895000 } { "5 GHz"; break } { $_ -ge 5925000 -and $_ -le 7125000 } { "6 GHz"; break } Default { $ghz = [math]::Floor($_ / 100000) / 10 "$ghz GHz" } } } $ScriptBlockWifiReconnectionKind = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Gebruik de volledige Enum waarden in een array $PossibleValues = @( [Windows.Devices.WiFi.WiFiReconnectionKind]::Automatic, [Windows.Devices.WiFi.WiFiReconnectionKind]::Manual ) # Filteren op basis van de string-waarde van het Enum object $PossibleValues | Where-Object { $_.ToString() -like "$wordToComplete*" } | ForEach-Object { # Door een CompletionResult te maken, geef je PowerShell de juiste metadata [System.Management.Automation.CompletionResult]::new( $_.ToString(), # De tekst die in de console verschijnt $_.ToString(), # De waarde die daadwerkelijk wordt ingevuld 'ParameterValue', # Het type resultaat $_.ToString() # De tooltip (hover tekst) ) } } $ScriptBlockCheckRadioOff = { if (-not (Get-RadioState | Where-Object { $_.Kind -eq 'WiFi' -and $_.State -eq 'On' })) { return [System.Management.Automation.CompletionResult]::new( '#Radio_is_OFF#', '--- RADIO IS OFF ---', 'ParameterValue', 'Use: Get-RadioState | Set-RadioState -SetState On' ) } } $ScriptBlockCheckLocationAccess = { if ((Request-LocationPermission -Sillent) -ne [Windows.Devices.Geolocation.GeolocationAccessStatus]::Allowed) { return [System.Management.Automation.CompletionResult]::new( '#LocationBlocked#', '--- LocationBlocked ---', 'ParameterValue', 'Use: Enable Desktop-App Location in Privacy & Security \ Location' ) } } $ScriptblockSmartScan = { # --- SMART SCAN LOGICA --- $counts = foreach ( $a in Get-WiFiAvailableAdapter ) { ($a.NetworkReport.AvailableNetworks | Measure-Object).Count } $cacheIsOld = ($null -eq $script:WiFiLastScanTime -or $script:WiFiLastScanTime -lt (Get-Date).AddMinutes(-5)) $anyAdapterEmpty = ($counts -le 1) if ($cacheIsOld -or $anyAdapterEmpty) { $null = Search-WifiNetworks } } $ScriptBlockSsid = { param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # 1. Directe Exit bij Hidden # Als de gebruiker al -Hidden heeft getypt, kunnen we geen SSID's suggereren $LocationAccess = &$ScriptBlockCheckLocationAccess if ($LocationAccess) { return $LocationAccess } $radioStatus = &$ScriptBlockCheckRadioOff if ($radioStatus) { return $radioStatus } if ($fakeBoundParameters.ContainsKey('Hidden')) { return [System.Management.Automation.CompletionResult]::new(" ", "SSID Unknow (Hidden Mode)", 'Text', "With a hidden network you need to enter the SSID manualy") } # --- SMART SCAN --- &$ScriptblockSmartScan $allWifiAdapters = Get-WiFiAvailableAdapter $targetIndices = if ($fakeBoundParameters.ContainsKey('Index')) { $fakeBoundParameters.Index } else { 0..($allWifiAdapters.Count - 1) } $givenBssid = $fakeBoundParameters['Bssid'] # 2. Haal netwerken op (Filtering op BSSID & overslaan van lege SSIDs) $networks = foreach ($i in $targetIndices) { if ($null -eq $allWifiAdapters[$i]) { continue } $allWifiAdapters[$i].NetworkReport.AvailableNetworks | Sort-Object NetworkRssiInDecibelMilliwatts -Descending | Where-Object { # Sla lege/verborgen netwerken over in de SSID-lijst if ([string]::IsNullOrWhiteSpace($_.Ssid)) { return $false } if ($givenBssid) { $cleanGiven = $givenBssid -replace '[^0-9A-Fa-f]' $cleanCurrent = $_.Bssid -replace '[^0-9A-Fa-f]' $cleanCurrent -eq $cleanGiven } else { $true } } } # 3. Bouw de lijst & Sorteer op signaalsterkte $networks | Group-Object Ssid | ForEach-Object { $strongest = $_.Group | Select-Object -First 1 $ssidValue = if ($strongest.Ssid -match ' ') { "`"$($strongest.Ssid)`"" } else { $strongest.Ssid } $displayText = "$($strongest.Ssid) ($($strongest.SignalBars * 20)% / $($strongest.NetworkRssiInDecibelMilliwatts) dBm)" if ($strongest.Ssid -like "$wordToComplete*") { [System.Management.Automation.CompletionResult]::new($ssidValue, $displayText, 'ParameterValue', "BSSID: $($strongest.Bssid)") } } } $ScriptBlockBssid = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $LocationAccess = &$ScriptBlockCheckLocationAccess if ($LocationAccess) { return $LocationAccess } $radioStatus = &$ScriptBlockCheckRadioOff if ($radioStatus) { return $radioStatus } # 1. Voorbereiding $filterSsid = if ($fakeBoundParameters.ContainsKey('Ssid')) { $fakeBoundParameters['Ssid'] -replace '["'']', '' } else { $null } $showHidden = $fakeBoundParameters.ContainsKey('Hidden') # --- SMART SCAN --- &$ScriptblockSmartScan $allAvailableNetworks = Get-WifiAvailableNetworks | Sort-Object NetworkRssiInDecibelMilliwatts -Descending $results = New-Object System.Collections.Generic.List[System.Management.Automation.CompletionResult] $foundMatch = $false # 3. De 'Kook-Logica' (Nauwkeurige Filter) foreach ($net in $allAvailableNetworks) { $isSsidEmpty = [string]::IsNullOrWhiteSpace($net.Ssid) $hasSsidFilter = -not [string]::IsNullOrWhiteSpace($filterSsid) # De 'Match' direct bepalen (First-match-wins door 'break') $isMatch = switch ($true) { ($hasSsidFilter -and $showHidden) { $isSsidEmpty; break } # Scenario 3 ($hasSsidFilter) { ($net.Ssid -eq $filterSsid); break } # Scenario 2 ($showHidden) { $isSsidEmpty; break } # Scenario 1b default { (-not $isSsidEmpty); break } # Scenario 1a } if ($isMatch) { $foundMatch = $true $label = if ($isSsidEmpty) { "$($net.Bssid) [HIDDEN]" } else { "$($net.Bssid) ($($net.Ssid))" } $results.Add([System.Management.Automation.CompletionResult]::new( $net.Bssid, $label, 'ParameterValue', "Signaal: $($net.SignalBars * 20)% ($($net.NetworkRssiInDecibelMilliwatts) dBm) Band ($(Get-WifiBandName $net.ChannelCenterFrequencyInKilohertz))" )) } } # 4. De Fallback (Blokkeert de standaard help van de CLI) if (-not $foundMatch) { # We voegen de spatie toe. Omdat we deze returnen, 'denkt' PS dat er een resultaat is # en stopt hij met het tonen van willekeurige commandline history/help. $results.Add([System.Management.Automation.CompletionResult]::new(" ", "No match in range", 'Text', "No netwerk found with this filter")) } return $results | Where-Object { [string]::IsNullOrEmpty($wordToComplete) -or $_.CompletionText -like "$wordToComplete*" } } $ScriptBlockIndex = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) Process { $Wifiadapters = Get-WiFiAvailableAdapter -TimeoutMs 1000 #this should be instant for ($i = 0; $i -lt $Wifiadapters.Count; $i++) { $id = $Wifiadapters[$i].NetworkAdapter.NetworkAdapterId $name = (Get-NetAdapter | Where-Object DeviceID -match $id).InterfaceDescription [System.Management.Automation.CompletionResult]::new($i, $i, 'ParameterValue', "Adapter $i : $name") } } } $ScriptBlockRadioState = { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) $States = @('On', 'Off') # Meest gebruikte WinRT RadioStates $States | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Set radio to $_") } } $ScriptBlockDeviceName = { param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) # Haal alle netwerkadapters op $Netadapters = Get-NetAdapter | Where-Object { $_.Name -eq "Wi-Fi" } | Select-Object InterfaceDescription, InstanceID foreach ($Netadapter in $Netadapters) { # Alleen tonen als de naam matcht met wat er al getypt is if ($Netadapter.InterfaceDescription -like "$wordToComplete*") { [System.Management.Automation.CompletionResult]::new( "'$($Netadapter.InterfaceDescription)'", # 1. CompletionText: This is what lands in the function (GUID) $Netadapter.InterfaceDescription, # 2. ListItemText: This is what the user sees 'ParameterValue', # 3. ResultType "Naam: $($Netadapter.InterfaceDescription)" # 4. ToolTip: Extra info at hover ) } } } Register-ArgumentCompleter -CommandName 'Get-WifiAvailableNetworks' -ParameterName 'DeviceName' -ScriptBlock $ScriptBlockDeviceName Register-ArgumentCompleter -CommandName 'Set-RadioState' -ParameterName 'SetState' -ScriptBlock $ScriptBlockRadioState Register-ArgumentCompleter -CommandName 'Disconnect-WifiNetwork' -ParameterName 'Index' -ScriptBlock $ScriptBlockIndex Register-ArgumentCompleter -CommandName 'Get-WifiCurrentConnection' -ParameterName 'Index' -ScriptBlock $ScriptBlockIndex Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Index' -ScriptBlock $ScriptBlockIndex Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Bssid' -ScriptBlock $ScriptBlockBssid Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Ssid' -ScriptBlock $ScriptBlockSSid Register-ArgumentCompleter -CommandName 'Connect-wifiNetwork' -ParameterName 'WifiReconnectionKind' -ScriptBlock $ScriptBlockWifiReconnectionKind Export-ModuleMember Get-RadioState Export-ModuleMember Set-RadioState Export-ModuleMember Get-WifiDeviceInformation Export-Modulemember Get-WiFiAvailableAdapter Export-ModuleMember Get-WifiAvailableNetworks Export-ModuleMember Search-WifiNetworks Export-ModuleMember Get-WifiConnectionProfile Export-Modulemember Remove-WifiConnectionProfile Export-ModuleMember Get-WifiCurrentConnection Export-ModuleMember Disconnect-WifiNetwork Export-ModuleMember Connect-WiFiNetwork Export-ModuleMember Request-LocationPermission |