functions/public/Get-KlippyFan.ps1

function Get-KlippyFan {
    <#
    .SYNOPSIS
        Gets fan status from a Klipper printer.

    .DESCRIPTION
        Retrieves the status of all fans configured on the printer.
        Supports various fan types: fan, heater_fan, controller_fan,
        temperature_fan, and fan_generic.

    .PARAMETER Id
        The unique identifier of the printer.

    .PARAMETER PrinterName
        The friendly name of the printer.

    .PARAMETER InputObject
        A printer object from pipeline input.

    .PARAMETER Name
        Filter by specific fan name. Supports wildcards.

    .PARAMETER Type
        Filter by fan type (Fan, HeaterFan, ControllerFan, TemperatureFan, FanGeneric).

    .EXAMPLE
        Get-KlippyFan
        Gets all fans from the default printer.

    .EXAMPLE
        Get-KlippyFan -PrinterName "voronv2" -Type HeaterFan
        Gets all heater fans from the specified printer.

    .EXAMPLE
        Get-KlippyFan -Name "*hotend*"
        Gets fans with "hotend" in the name.

    .OUTPUTS
        KlippyCLI.Fan objects.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(ParameterSetName = 'ById')]
        [ValidateNotNullOrEmpty()]
        [string]$Id,

        [Parameter(ParameterSetName = 'ByName', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$PrinterName,

        [Parameter(ParameterSetName = 'ByObject', ValueFromPipeline = $true)]
        [PSCustomObject]$InputObject,

        [Parameter()]
        [string]$Name,

        [Parameter()]
        [ValidateSet('Fan', 'HeaterFan', 'ControllerFan', 'TemperatureFan', 'FanGeneric')]
        [string]$Type
    )

    process {
        # Resolve printer
        $resolveParams = @{}
        switch ($PSCmdlet.ParameterSetName) {
            'ById' { $resolveParams['Id'] = $Id }
            'ByName' { $resolveParams['PrinterName'] = $PrinterName }
            'ByObject' { $resolveParams['InputObject'] = $InputObject }
        }

        $printer = Resolve-KlippyPrinterTarget @resolveParams

        try {
            # Get list of available objects
            $allObjects = Invoke-KlippyJsonRpc -Printer $printer -Method "printer/objects/list"

            # Define fan object prefixes
            $fanPrefixes = @{
                'fan'              = 'Fan'
                'heater_fan'       = 'HeaterFan'
                'controller_fan'   = 'ControllerFan'
                'temperature_fan'  = 'TemperatureFan'
                'fan_generic'      = 'FanGeneric'
            }

            # Filter objects that are fans
            $fanObjects = foreach ($obj in $allObjects.Objects) {
                foreach ($prefix in $fanPrefixes.Keys) {
                    if ($obj -eq $prefix -or $obj.StartsWith("${prefix} ")) {
                        [PSCustomObject]@{
                            ObjectName = $obj
                            FanType    = $fanPrefixes[$prefix]
                            FanName    = if ($obj -eq $prefix) { $prefix } else { ($obj -split ' ', 2)[1] }
                        }
                        break
                    }
                }
            }

            # Apply type filter
            if ($Type) {
                $fanObjects = $fanObjects | Where-Object { $_.FanType -eq $Type }
            }

            # Apply name filter
            if ($Name) {
                $fanObjects = $fanObjects | Where-Object { $_.FanName -like $Name }
            }

            if (-not $fanObjects) {
                Write-Verbose "No fans found matching the specified criteria."
                return
            }

            # Query each fan object
            $queryObjects = @{}
            foreach ($fan in $fanObjects) {
                $queryObjects[$fan.ObjectName] = $null
            }

            # Build query string
            $queryParts = $queryObjects.Keys | ForEach-Object { [System.Uri]::EscapeDataString($_) }
            $endpoint = "printer/objects/query?" + ($queryParts -join '&')

            $response = Invoke-KlippyJsonRpc -Printer $printer -Method $endpoint -NoNormalize

            # Process results
            foreach ($fan in $fanObjects) {
                $data = $response.status.($fan.ObjectName)
                if ($data) {
                    $result = [PSCustomObject]@{
                        PSTypeName  = 'KlippyCLI.Fan'
                        PrinterId   = $printer.Id
                        PrinterName = $printer.PrinterName
                        Name        = $fan.FanName
                        Type        = $fan.FanType
                        Speed       = if ($null -ne $data.speed) { [math]::Round($data.speed * 100, 1) } else { $null }
                        RPM         = $data.rpm
                    }

                    # Add type-specific properties
                    if ($fan.FanType -eq 'TemperatureFan') {
                        $result | Add-Member -NotePropertyName 'Temperature' -NotePropertyValue $data.temperature -Force
                        $result | Add-Member -NotePropertyName 'Target' -NotePropertyValue $data.target -Force
                    }

                    $result
                }
            }
        }
        catch {
            Write-Error "Failed to get fans from '$($printer.PrinterName)': $_"
        }
    }
}