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-WifiAvailableNetworks
 .Parameter Disconnect-WifiNetwork
 .Parameter Connect-WiFiNetwork
#>


ImportSystemModules
Add-Type -AssemblyName System.Runtime.WindowsRuntime

$asTaskGeneric=([System.WindowsRuntimeSystemExtensions].GetMethods()|Where-Object{$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'})[0]
Function Await($WinRtTask, $ResultType){
  $asTask=$asTaskGeneric.MakeGenericMethod($ResultType)
  $netTask=$asTask.Invoke($null, @($WinRtTask))
  $netTask.Wait(-1)|Out-Null
  return $netTask.Result
}

[Windows.Devices.Radios.Radio,Windows.System.Devices,ContentType=WindowsRuntime] | Out-Null
[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
  Haalt alle beschikbare radio-adapters op (WiFi, Bluetooth, Mobiel) en toont hun huidige status.
 
.DESCRIPTION
  Deze functie gebruikt de Windows Runtime (WinRT) API om directe toegang te krijgen tot de radio-hardware van het systeem.
  Het resultaat is een lijst van Radio-objecten die direct gepiped kunnen worden naar Set-RadioState.
 
.EXAMPLE
  Get-RadioState
  Toont alle radio's en of ze 'On' of 'Off' zijn.
 
.EXAMPLE
  Get-RadioState | Where-Object Kind -eq 'WiFi' | Set-RadioState -SetState Off
  Zoekt specifiek de WiFi-radio en schakelt deze uit.
 
.OUTPUTS
  Windows.Devices.Radios.Radio
#>

  [outputType([Windows.Devices.Radios.Radio])]
  param()
  
  $accessStatus = Await ([Windows.Devices.Radios.Radio]::RequestAccessAsync()) ([Windows.Devices.Radios.RadioAccessStatus])
  
  if($accessStatus -eq "Allowed"){
    Await ([Windows.Devices.Radios.Radio]::GetRadiosAsync()) ([System.Collections.Generic.IReadOnlyList[Windows.Devices.Radios.Radio]])
  } else {
    Write-Warning "Geen toegang tot de radio-hardware. Status: $accessStatus" 
  }
}

Function Set-RadioState{
<#
.SYNOPSIS
  Schakelt een specifieke radio aan of uit.
 
.DESCRIPTION
  Ontvangt Radio-objecten (meestal via de pipeline van Get-RadioState) en
  probeert de fysieke status te wijzigen naar 'On' of 'Off'.
 
.PARAMETER SetState
  De gewenste doelstatus. Gebruik 'On' of 'Off'. (Heeft autocomplete support).
 
.PARAMETER Radios
  Het Radio-object dat moet worden aangepast. Accepteert input via de pipeline.
 
.EXAMPLE
  Get-RadioState | Set-RadioState -SetState Off
  Get-Radiostate | where-object Name -eq 'Wi-Fi'| Set-RadioSTate -SetState On
  Probeert alle radio's op het systeem uit te schakelen.
 
.NOTES
  Sommige adapters (zoals Mobile Broadband) kunnen de statuswijziging weigeren
  als deze door het systeem elders beheerd worden. De functie zal in dat geval
  een waarschuwing geven in plaats van te crashen.
#>

  param(
    [Parameter(Mandatory=$true)][ArgumentCompleter({
      param($commandName,$parameterName,$wordToComplete,$commandAst,$fakeBoundParameters)
        $possibleValues=@{Values=@([Windows.Devices.Radios.RadioState]::On,[Windows.Devices.Radios.RadioState]::Off)}
      if($fakeBoundParameters.ContainsKey('Type')){
        $possibleValues[$fakeBoundParameters.Type]| Where-object {$_ -like "$wordToComplete*"}
      }else{
        $possibleValues.Values|Foreach-Object{$_}
      }
    })]$SetState,
    [parameter(Mandatory=$true,ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][Windows.Devices.Radios.Radio][alias("Radio")][array]$Radios
  )
  begin {
  }
  process{
    $Radios.Gettype()
    Await ($Radios.SetStateAsync($SetState)) ([Windows.Devices.Radios.RadioAccessStatus])
  } 
  end{}
}

[Windows.Devices.WiFi.WiFiAdapter,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()
  Await([Windows.Devices.Enumeration.DeviceInformation]::FindAllAsync([Windows.Devices.WiFi.WiFiAdapter]::GetDeviceSelector()))([Windows.Devices.Enumeration.DeviceInformationCollection])
}

Function Get-WiFiAvailableAdapter{
  #[outputtype([Windows.Devices.WiFi.WiFiAdapter],ParameterSetName="WiFiAvailableAdapter")]
  [outputtype([Windows.Devices.WiFi.WiFiAdapter])]
  param()
  Await([Windows.Devices.WiFi.WiFiAdapter]::FindAllAdaptersAsync())([System.Collections.Generic.IReadOnlyList[Windows.Devices.WiFi.WiFiAdapter]])
}

Function Get-WifiConnectionProfile{
  [outputType([Windows.Networking.Connectivity.NetworkInformation])]
  param()
  [Windows.Networking.Connectivity.NetworkInformation]::GetConnectionProfiles()|Where-Object{$_.IsWlanConnectionProfile}
}

Function Remove-WifiConnectionProfile{
  param(
  [parameter(Mandatory=$true,ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][Windows.Networking.Connectivity.ConnectionProfile]
  [alias("Profile")]
  [array]$Profiles
  )
  begin {}
  process{
  if($Profiles.CanDelete){
    Write-host ("Delete Profile {0}" -f $Profiles.ProfileName)
     $Profiles.TryDeleteAsync() | Out-Null
  }else{
    Write-host ("Can't Delete Profile {0}" -f $Profiles.ProfileName)
  }
  } 
  end{}
}

Function Get-WifiCurrentConection{
  [outputtype([Windows.Networking.Connectivity.ConnectionProfile])]
  param()
  await((Get-WiFiAvailableAdapter).NetworkAdapter.GetConnectedProfileAsync()) ([Windows.Networking.Connectivity.ConnectionProfile])
}

Function Disconnect-WifiNetwork {
  param(
    [Parameter(Mandatory=$false)]
    [int[]]$Index # Veranderd naar array voor meerdere indexen (bijv. 0, 2)
  )

  $Adapters = Get-WiFiAvailableAdapter

  if ($null -eq $Adapters -or $Adapters.Count -eq 0) {
    Write-Warning "Geen WiFi-adapters gevonden om te verbreken."
    return
  }

  if ($PSBoundParameters.ContainsKey('Index')) {
    # Loop door elke opgegeven index heen
    foreach ($i in $Index) {
      if ($i -ge 0 -and $i -lt $Adapters.Count) {
        $adapterName = (Get-NetAdapter | Where-Object DeviceID -match $Adapters[$i].NetworkAdapter.NetworkAdapterId).InterfaceDescription
        Write-Host "Verbinding verbreken op adapter index: $i : $adapterName" -ForegroundColor Cyan
        $Adapters[$i].Disconnect()
      } else {
        Write-Warning "Index $i overgeslagen: bestaat niet (beschikbaar: 0 t/m $($Adapters.Count - 1))."
      }
    }
  } 
  else {
    # DEFAULT: Alles platgoooien
    Write-Host "Verbinding verbreken op ALLE ($($Adapters.Count)) adapters..." -ForegroundColor Yellow
    $Adapters.Disconnect()
  }
}

Register-ArgumentCompleter -CommandName 'Disconnect-WifiNetwork' -ParameterName 'Index' -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $adapters = Get-WiFiAvailableAdapter
    for ($i = 0; $i -lt $adapters.Count; $i++) {
        $id = $adapters[$i].NetworkAdapter.NetworkAdapterId
        $name = (Get-NetAdapter | Where-Object DeviceID -match $id).InterfaceDescription
        [System.Management.Automation.CompletionResult]::new($i, $i, 'ParameterValue', "Adapter $i : $name")
    }
}

Function Get-WifiAvailableNetworks{
  (Get-WiFiAvailableAdapter).NetworkReport.AvailableNetworks
}

Function Connect-WiFiNetwork {
  <#
.SYNOPSIS
  Verbindt één, meerdere of alle WiFi-adapters met een specifiek netwerk (SSID).
 
.DESCRIPTION
  Deze functie gebruikt de WinRT API om verbinding te maken. Het ondersteunt het
  gelijktijdig aansturen van specifieke adapters via hun index. Indien een netwerk
  al bekend is bij Windows (profiel aanwezig), is het wachtwoord niet verplicht.
 
.PARAMETER Ssid
  De naam van het WiFi-netwerk. Gebruik de TAB-toets voor suggesties van
  beschikbare netwerken in de buurt.
 
.PARAMETER Password
  Het wachtwoord voor het netwerk. Dit is een dynamische parameter die
  SecureString gebruikt (tenzij -AsPlainText is opgegeven). Niet verplicht
  als er al een opgeslagen profiel bestaat.
 
.PARAMETER Index
  De index van de adapter(s) (bijv. 0, 1). Laat dit leeg om de verbinding
  op ALLE beschikbare WiFi-adapters tegelijk te proberen.
 
.PARAMETER User
  Optionele gebruikersnaam voor enterprise-netwerken (802.1X).
 
.PARAMETER Bssid
  Forceer verbinding met een specifiek Access Point op basis van het MAC-adres.
 
.EXAMPLE
  Connect-WiFiNetwork -Ssid "MijnThuisNetwerk" -Index 0
  Verbindt de eerste adapter met het opgegeven netwerk.
 
.EXAMPLE
  Connect-WiFiNetwork -Ssid "Open_WiFi"
  Probeert alle WiFi-adapters op het systeem te verbinden met een open netwerk.
 
.EXAMPLE
  Connect-WiFiNetwork -Ssid "Bedrijf_WiFi" -Password (Read-Host -AsSecureString) -Index 0, 2
  Verbindt adapter 0 en 2 met een beveiligd netwerk met handmatige wachtwoordinvoer.
 
.NOTES
  Versie: 2.0.0.0
  De functie controleert per adapter of de SSID zichtbaar is voordat de
  verbinding wordt gestart.
#>

  [CmdletBinding()]
  param(
    [string]$User,
    [string]$Bssid,
    [Switch]$AsPlainText,
    [Parameter(Mandatory=$true)]$Ssid, 
    [int[]]$Index
  )
  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
  }
  Process{
    $PasswordValue = $PSBoundParameters['Password']
    $SsidValue = $PSBoundParameters['Ssid']

    # 1. Haal de adapters op
    $AllAdapters = Get-WiFiAvailableAdapter
    
    # 2. Bepaal op welke adapters we actie ondernemen
    $TargetAdapters = if ($PSBoundParameters.ContainsKey('Index')) {
        foreach($i in $Index) { $AllAdapters[$i] }
    } else {
        $AllAdapters # 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
    }

  $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 "Verbinden via 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 = Await($_.ConnectAsync($Network,[Windows.Devices.WiFi.WiFiReconnectionKind]::Automatic,$Credentials,$null))([Windows.Devices.WiFi.WiFiConnectionResult])
    
    # Optioneel: Laat direct weten of het gelukt is voor deze specifieke kaart
    $statusColor = "Yellow"
    if ($Result.ConnectionStatus -eq "Success") { $statusColor = "Green" }
    Write-Host "Status voor $adapterName : $($Result.ConnectionStatus)" -ForegroundColor $statusColor
      [PSCustomObject]@{
          ConnectionStatus = $Result.ConnectionStatus  # NIET hernoemen!
          AdapterName      = $adapterName
          Ssid             = $SsidValue
          Index            = [array]::IndexOf($AllAdapters, $_)
          Bssid            = $Network.Bssid
      }
    }
  }
}

Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Ssid' -ScriptBlock {
    param ($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    
    # 1. Haal de adapters op die de gebruiker heeft opgegeven (of allemaal)
    $allAdapters = Get-WiFiAvailableAdapter
    $targetIndices = if ($fakeBoundParameters.ContainsKey('Index')) { $fakeBoundParameters.Index } else { 0..($allAdapters.Count - 1) }

    # 2. Haal de netwerken op SPECIFIEK voor deze adapters
    $networks = foreach ($i in $targetIndices) {
        $allAdapters[$i].NetworkReport.AvailableNetworks | 
            Where-Object { -not [string]::IsNullOrEmpty($_.Ssid) }
    }

    # 3. Maak de unieke lijst van SSIDs
    $possibleValues = $networks | Select-Object -ExpandProperty Ssid -Unique | ForEach-Object {
        $formattedSsid = if ($_ -match ' ') { "`"$_`"" } else { $_ }
        [PscustomObject]@{ Ssid = $formattedSsid }
    }

    # 4. Filter op wat de gebruiker typt
    if ([string]::IsNullOrEmpty($wordToComplete)) {
        $possibleValues | Select-Object -ExpandProperty Ssid
    } else {
        # Fix: Zorg dat we op de juiste property filteren
        $possibleValues | Where-Object { $_.Ssid -like "$wordToComplete*" } | Select-Object -ExpandProperty Ssid
    }
}

Register-ArgumentCompleter -CommandName 'Connect-WiFiNetwork' -ParameterName 'Index' -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
    $adapters = Get-WiFiAvailableAdapter
    for ($i = 0; $i -lt $adapters.Count; $i++) {
        $name = $adapters[$i].NetworkAdapter.NetworkItem.NetworkId # Of gebruik Get-NetAdapter voor de 'echte' naam
        [System.Management.Automation.CompletionResult]::new($i, $i, 'ParameterValue', "Adapter $i : $name")
    }
}

Export-ModuleMember Get-RadioState
Export-ModuleMember Set-RadioState
Export-ModuleMember Get-WifiDeviceInformation
Export-Modulemember Get-WiFiAvailableAdapter
Export-ModuleMember Get-WifiAvailableNetworks
Export-ModuleMember Get-WifiConnectionProfile
Export-Modulemember Remove-WifiConnectionProfile
Export-ModuleMember Get-WifiCurrentConection
Export-ModuleMember Disconnect-WifiNetwork
Export-ModuleMember Connect-WiFiNetwork