functions/public/Get-KlippyUsbDevice.ps1

function Get-KlippyUsbDevice {
    <#
    .SYNOPSIS
        Gets USB device information from a Klipper printer host.

    .DESCRIPTION
        Retrieves USB device details detected by the host running Moonraker.
        Tries the USB endpoint first and falls back to system info when needed.

    .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 Raw
        Return the raw response without normalization.

    .EXAMPLE
        Get-KlippyUsbDevice
        Gets USB devices from the default printer.

    .EXAMPLE
        Get-KlippyUsbDevice -PrinterName "voronv2"
        Gets USB devices from the specified printer.

    .EXAMPLE
        Get-KlippyPrinter | Get-KlippyUsbDevice
        Gets USB devices from all registered printers.

    .OUTPUTS
        PSCustomObject with USB device information.
    #>

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

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

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

        [Parameter()]
        [switch]$Raw
    )

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

        $printer = Resolve-KlippyPrinterTarget @resolveParams

        try {
            Write-Verbose "[$($printer.PrinterName)] Fetching USB device info..."

            $usb = $null
            try {
                $usb = Invoke-KlippyJsonRpcMethod -Printer $printer -Method "machine.peripherals.usb" -NoNormalize:$Raw
            }
            catch {
                if ($_.Exception.Message -notmatch 'Method not found|Not Found') {
                    throw
                }
            }

            if ($usb) {
                if ($usb -is [array]) {
                    $usbDevices = $usb
                }
                elseif ($usb.UsbDevices) {
                    $usbDevices = $usb.UsbDevices
                }
                elseif ($usb.Devices) {
                    $usbDevices = $usb.Devices
                }
            }

            if (-not $usbDevices) {
                try {
                    $usb = Invoke-KlippyJsonRpc -Printer $printer -Method "machine/usb" -NoNormalize:$Raw
                }
                catch {
                    if ($_.Exception.Message -notmatch 'Not Found') {
                        throw
                    }
                }

                if ($usb) {
                    if ($usb -is [array]) {
                        $usbDevices = $usb
                    }
                    elseif ($usb.UsbDevices) {
                        $usbDevices = $usb.UsbDevices
                    }
                    elseif ($usb.Devices) {
                        $usbDevices = $usb.Devices
                    }
                }
            }

            if (-not $usbDevices) {
                $sysInfo = Invoke-KlippyJsonRpc -Printer $printer -Method "machine/system_info" -NoNormalize:$Raw
                $usbDevices = $sysInfo.SystemInfo.UsbDevices
                if (-not $usbDevices -and $sysInfo.SystemInfo.Usb) {
                    $usbDevices = $sysInfo.SystemInfo.Usb
                }
            }

            if ($Raw) {
                return $usbDevices
            }

            if (-not $usbDevices -or $usbDevices.Count -eq 0) {
                Write-Verbose "[$($printer.PrinterName)] No USB devices found"
                return
            }

            foreach ($device in $usbDevices) {
                [PSCustomObject]@{
                    PSTypeName   = 'KlippyCLI.UsbDevice'
                    PrinterId    = $printer.Id
                    PrinterName  = $printer.PrinterName
                    BusNumber    = $device.BusNum ?? $device.bus_num ?? $device.BusNumber ?? $device.bus_number
                    DeviceNumber = $device.DeviceNum ?? $device.device_num ?? $device.DeviceNumber ?? $device.device_number
                    Location     = $device.Location ?? $device.location ?? $device.UsbLocation ?? $device.usb_location
                    VendorId     = $device.VendorId ?? $device.vendor_id ?? $device.Vendor ?? $device.vendor
                    ProductId    = $device.ProductId ?? $device.product_id ?? $device.Product ?? $device.product
                    Manufacturer = $device.Manufacturer ?? $device.manufacturer ?? $device.MfgString ?? $device.mfg_string
                    Product      = $device.ProductDesc ?? $device.product_desc ?? $device.Description ?? $device.description ?? $device.Product ?? $device.product
                    Serial       = $device.SerialNum ?? $device.serial_num ?? $device.Serial ?? $device.serial
                    Class        = $device.Class ?? $device.class ?? $device.DeviceClass ?? $device.device_class
                    SubClass     = $device.SubClass ?? $device.subclass ?? $device.DeviceSubclass ?? $device.device_subclass
                    Protocol     = $device.Protocol ?? $device.protocol ?? $device.DeviceProtocol ?? $device.device_protocol
                }
            }
        }
        catch {
            Write-Error "[$($printer.PrinterName)] Failed to get USB device info: $_"
        }
    }
}