Core/Public/Wifi/Connect-WifiNetwork.ps1

Function Connect-WifiNetwork {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]$Ssid, 
        [string]$Bssid,
        [string]$User,
        [Switch]$AsPlainText,
        [Switch]$Hidden,
        [int[]]$Index,
        [Windows.Devices.WiFi.WiFiReconnectionKind]$WifiReconnectionKind = [Windows.Devices.WiFi.WiFiReconnectionKind]::Automatic
    )
    DynamicParam {
        # Choice type depending on Switch
        $parameterType = if ($AsPlainText) { [string] } else { [securestring] }
        # Create the attributeCollection for all attributes like Mandatory
        $attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
        $attribute = New-Object System.Management.Automation.ParameterAttribute
        $attribute.Mandatory = $false
        $attributeCollection.Add($attribute)
        # Define the 'Password' paramter at runtime
        $dynParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Password', $parameterType, $attributeCollection)
        # Add to the Dictionary wich powreshell expects to receive
        $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
        $paramDictionary.Add('Password', $dynParam)
        return $paramDictionary
    }
    Process {
        
        $Adapters = Get-WiFiAvailableAdapters
        if (-not $Adapters) { throw "No active WiFi-adapter found." }
        
        $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. Prepair Credentials ---
        $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. Getting the adapters you want to control ---
        $Targets = if ($PSBoundParameters.ContainsKey('Index')) { $Index | ForEach-Object { $Adapters[$_] } } else { $Adapters }
        # --- 4. Connection Loop ---
        $Targets | ForEach-Object {  
            $currentId = $_.NetworkAdapter.NetworkAdapterId
            $name = (Get-NetAdapter | Where-Object DeviceID -match $currentId).InterfaceDescription
      
            # Search the network specifick for this
            $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) {
                # Force Scan if not found
                $null = Get-wifiAvailableNetworks
                $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()
            $_Ssid = $null
            if ($Hidden) {
                $_Ssid = $Ssid  # Pass the SSID if it's a hidden network
            }
       
            $ConnectAsync = $_.ConnectAsync($Net, $wifiReconnectionKind, $creds, $_Ssid) 
            start-sleep -Milliseconds 50
            while ($ConnectAsync.Status -eq [Windows.Foundation.AsyncStatus]::Started) { start-sleep -Milliseconds 100 } 
            $Results = $ConnectAsync.GetResults()
            
            [PSCustomObject]@{
                ConnectionStatus = $Results.ConnectionStatus
                AdapterName      = $name
                Ssid             = $Ssid
                Bssid            = $Net.Bssid
            }
        }
    }
}
<#$SsidCompleter = {
    param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
    try {
        $LocationAccess = &$ScriptBlockCheckLocationAccess
        if ($LocationAccess) { return $LocationAccess }
        $radioStatus = &$ScriptBlockCheckRadioOff
        if ($radioStatus) { return $radioStatus }
        # Haal de netwerken op
        $Networks = @(Get-WiFiAvailableNetworks)
         
        # Filter: Alleen SSIDs die NIET leeg/null zijn en overeenkomen met wat getypt is
        $Networks | Sort-Object NetworkRssiInDecibelMilliwatts -Descending | Where-Object {
            -not [string]::IsNullOrWhiteSpace($_.Ssid) -and
            $_.Ssid -like "$WordToComplete*"
        } | Select-Object -ExpandProperty Ssid -Unique | ForEach-Object {
            [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
        }
    } catch {}
}#>

$SsidCompleter = {
    param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
    try {
        $LocationAccess = &$ScriptBlockCheckLocationAccess
        if ($LocationAccess) { return $LocationAccess }
        $radioStatus = &$ScriptBlockCheckRadioOff
        if ($radioStatus) { return $radioStatus }

        # 1. GEt all networks and filter on input
        $Networks = @(Get-WiFiAvailableNetworks) | Where-Object { 
            -not [string]::IsNullOrWhiteSpace($_.Ssid) -and $_.Ssid -like "$WordToComplete*" 
        }

        # 2. Group per SSID
        $Groups = $Networks | Group-Object Ssid | ForEach-Object {
            [PSCustomObject]@{
                Ssid    = $_.Name
                Count   = $_.Count
                MaxRssi = ($_.Group | Measure-Object NetworkRssiInDecibelMilliwatts -Maximum).Maximum
            }
        }

        # 3. Sort groups on signal strenght
        $Groups | Sort-Object MaxRssi -Descending | ForEach-Object {
            [System.Management.Automation.CompletionResult]::new(
                $_.Ssid,                               # the value
                $_.Ssid,                               # Text in list
                'ParameterValue',                      # Type
                "Zichtbare netwerken: $($_.Count)"     # Tooltip with the number of networks nearby
            )
        }
    } catch {}
}
Register-ArgumentCompleter -CommandName 'Connect-WifiNetwork' -ParameterName 'Ssid' -ScriptBlock $SsidCompleter

$BssidCompleter = {
    param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
    try {
        $LocationAccess = &$ScriptBlockCheckLocationAccess
        if ($LocationAccess) { return $LocationAccess }
        $radioStatus = &$ScriptBlockCheckRadioOff
        if ($radioStatus) { return $radioStatus }
        # 1. Get A list of networks sort by signal strength strongest on top
        $Networks = @(Get-WiFiAvailableNetworks) | Sort-Object NetworkRssiInDecibelMilliwatts -Descending
        
        $IsHiddenRequested = $FakeBoundParameters.ContainsKey('Hidden')
        $SelectedSsid = $FakeBoundParameters['Ssid']

        $Networks | Where-Object {
            $HasNoSsid = [string]::IsNullOrWhiteSpace($_.Ssid)
            $MatchBssid = $_.Bssid -like "$WordToComplete*"

            if ($IsHiddenRequested) {
                 # show hidden networks (BSSIDS without name )
                $HasNoSsid -and $MatchBssid
            } else {
                # Show SSid only if have value
                $Condition = -not $HasNoSsid -and $MatchBssid
                # Filter extra on SSID if filled
                if ($SelectedSsid) {
                    $Condition = $Condition -and ($_.Ssid -eq $SelectedSsid)
                }
                $Condition
            }
        } | ForEach-Object {
            $Signal = "$($_.NetworkRssiInDecibelMilliwatts) dBm"
           
            $ToolTip = if ($IsHiddenRequested) { "Sterkte: $Signal [HIDDEN]" } else { "SSID: $($_.Ssid) | Sterkte: $Signal | Band $(Get-WifiBandName $_.ChannelCenterFrequencyInKilohertz)" }
            
            # CompletionResult: Value, Visual, type, tooltip
            [System.Management.Automation.CompletionResult]::new($_.Bssid, $_.Bssid, 'ParameterValue', $ToolTip)
        }
    } catch {}
}

Register-ArgumentCompleter -CommandName 'Connect-WifiNetwork' -ParameterName 'Bssid' -ScriptBlock $BssidCompleter

$ConnectIndexCompleter = {
    param($CommandName, $ParameterName, $WordToComplete, $CommandAst, $FakeBoundParameters)
    try {

        $AsyncOp = [Windows.Devices.WiFi.WiFiAdapter]::FindAllAdaptersAsync()
        while ($AsyncOp.Status -eq [Windows.Foundation.AsyncStatus]::Started) { Start-Sleep -Milliseconds 10 }
        $Adapters = @($AsyncOp.GetResults())

        $AlreadySelected = @()
        if ($FakeBoundParameters.ContainsKey('Index')) {
            $AlreadySelected = @($FakeBoundParameters['Index'])
        }

        for ($i = 0; $i -lt $Adapters.Count; $i++) {
            if ($i -in $AlreadySelected) { continue }

            $Name = $Adapters[$i].NetworkAdapter.NetworkAdapterId
            $CompletionText = $i.ToString()
            $ListItemText = "$i ($Name)"
            $ToolTip = "Verbind via adapter $($i): $Name"

            [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, 'ParameterValue', $ToolTip)
        }
    } catch {}
}

Register-ArgumentCompleter -CommandName 'Connect-WifiNetwork' -ParameterName 'Index' -ScriptBlock $ConnectIndexCompleter