Private/Kinds/Power.ps1

# The Power Kind: whether the machine is allowed to go fast.
#
# A laptop that is throttled by its power plan, its power mode or by running on battery
# looks identical to a slow laptop until someone checks.

# The GUID Windows uses for the Power saver plan, and the overlay GUIDs for the power
# mode slider. Invariant across languages, which is why they are matched rather than the
# display text - the Target Machine estate is German.
$script:PowerSaverSchemeGuid = 'a1841308-3541-4fab-bc81-f71556f20b4a'
$script:PowerOverlayNames = @{
    '961cc777-2547-4f9d-8174-7d86181b8a7a' = 'Best power efficiency'
    '3af9b8d9-7c97-431d-ad78-34a8bfea439f' = 'Better battery'
    '00000000-0000-0000-0000-000000000000' = 'Balanced'
    'ded574b5-45a0-4f42-8737-46345c09c238' = 'Best performance'
}

function Get-PowerData {
    [CmdletBinding()]
    [OutputType([psobject])]
    param([hashtable]$Parameters = @{})

    $scheme = ''
    try { $scheme = (powercfg /getactivescheme) -join ' ' } catch { }

    $schemes = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Power\User\PowerSchemes' -ErrorAction SilentlyContinue
    $battery = @(Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue)[0]

    [pscustomobject]@{
        PSTypeName              = 'Gutcheck.Data.Power'
        ActiveSchemeDescription = $scheme
        ActiveOverlayGuid       = $schemes.ActiveOverlayAcPowerScheme
        BatteryPresent          = [bool]$battery
        BatteryStatus           = $battery.BatteryStatus
        BatteryChargeRemaining  = $battery.EstimatedChargeRemaining
    }
}

function ConvertTo-PowerFinding {
    [CmdletBinding()]
    [OutputType([psobject])]
    param(
        [AllowNull()]$Data,
        [hashtable]$Parameters = @{}
    )

    New-PowerPlanFinding   -Data $Data -Parameters $Parameters
    New-PowerModeFinding   -Data $Data -Parameters $Parameters
    New-PowerSourceFinding -Data $Data -Parameters $Parameters
}

function New-PowerPlanFinding {
    [CmdletBinding()]
    param([AllowNull()]$Data, [hashtable]$Parameters)

    $description = [string](Get-DataProperty $Data 'ActiveSchemeDescription')
    if (-not $description) {
        return New-UnavailableFinding -Category Power -Check (Get-Text 'Check.Power.PowerPlan') `
            -Hint (Get-Text 'Hint.Power.TheActivePowerPlanCould')
    }

    $severity = 'OK'
    if ($description -match $script:PowerSaverSchemeGuid) { $severity = 'WARN' }

    New-Finding -Category Power -Check (Get-Text 'Check.Power.PowerPlan') -Severity $severity `
        -Value ($description -replace '^.*?:\s*', '') `
        -Hint (Get-Text 'Hint.Power.PowerSaverPlanCapsCPU')
}

function New-PowerModeFinding {
    [CmdletBinding()]
    param([AllowNull()]$Data, [hashtable]$Parameters)

    # Absent on desktops and on Windows builds without the power mode slider. The script
    # reported nothing at all in that case, and so does this.
    $guid = Get-DataProperty $Data 'ActiveOverlayGuid'
    if (-not $guid) { return }

    $mode = $script:PowerOverlayNames["$guid".ToLowerInvariant()]
    if (-not $mode) { $mode = "$guid" }

    $severity = 'OK'
    if ($mode -match 'efficiency|battery') { $severity = 'WARN' }

    New-Finding -Category Power -Check (Get-Text 'Check.Power.PowerModePluggedIn') -Severity $severity -Value $mode `
        -Hint (Get-Text 'Hint.Power.SetSettingsSystemPowerPower')
}

function New-PowerSourceFinding {
    [CmdletBinding()]
    param([AllowNull()]$Data, [hashtable]$Parameters)

    # A desktop has no battery and therefore no Finding, as in the script.
    if (-not (Get-DataProperty $Data 'BatteryPresent')) { return }

    $charge = Get-DataProperty $Data 'BatteryChargeRemaining'

    # Win32_Battery reports 1 for "discharging"; anything else means the machine is fed.
    $onAcPower = (ConvertTo-Number (Get-DataProperty $Data 'BatteryStatus')) -ne 1
    if ($onAcPower) {
        $value = "AC adapter (charge $charge %)"
        $severity = 'OK'
    }
    else {
        $value = "Battery ($charge %)"
        $severity = 'WARN'
    }

    New-Finding -Category Power -Check (Get-Text 'Check.Power.PowerSource') -Severity $severity -Value $value `
        -Hint (Get-Text 'Hint.Power.TestOnACOnBattery')
}