functions/public/Wait-KlippyReady.ps1

function Wait-KlippyReady {
    <#
    .SYNOPSIS
        Waits for a Klipper printer to become ready.

    .DESCRIPTION
        Monitors the printer state via WebSocket and waits until Klipper
        reports a "ready" state. Useful after firmware restarts or error recovery.

    .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 Timeout
        Maximum time to wait in seconds. Default is 120.

    .PARAMETER Quiet
        Suppress progress messages.

    .EXAMPLE
        Wait-KlippyReady -PrinterName "voronv2"
        Waits for the printer to become ready.

    .EXAMPLE
        Wait-KlippyReady -Timeout 60
        Waits up to 60 seconds for the default printer to become ready.

    .EXAMPLE
        Restart-KlippyService -ServiceName "klipper" | Wait-KlippyReady
        Restarts Klipper and waits for it to become ready.

    .OUTPUTS
        PSCustomObject with the ready state 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()]
        [ValidateRange(1, 3600)]
        [int]$Timeout = 120,

        [Parameter()]
        [switch]$Quiet
    )

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

        $printer = Resolve-KlippyPrinterTarget @resolveParams

        # First check current state via REST
        try {
            $currentInfo = Invoke-KlippyJsonRpc -Printer $printer -Method "printer/info"
            if ($currentInfo.State -eq 'ready') {
                if (-not $Quiet) {
                    Write-Host "[$($printer.PrinterName)] Already ready"
                }
                return [PSCustomObject]@{
                    PSTypeName  = 'KlippyCLI.WaitResult'
                    PrinterId   = $printer.Id
                    PrinterName = $printer.PrinterName
                    State       = 'ready'
                    WaitTime    = [TimeSpan]::Zero
                    Success     = $true
                }
            }
        }
        catch {
            Write-Verbose "Could not get current state, will wait via WebSocket: $_"
        }

        if (-not $Quiet) {
            Write-Host "[$($printer.PrinterName)] Waiting for printer to become ready..." -NoNewline
        }

        $startTime = [datetime]::UtcNow
        $ws = $null

        try {
            $ws = New-KlippyWebSocketClient -Printer $printer

            # Subscribe to webhooks for state changes
            $null = $ws.Subscribe(@{ webhooks = $null; print_stats = $null })

            $deadline = [datetime]::UtcNow.AddSeconds($Timeout)

            while ([datetime]::UtcNow -lt $deadline) {
                $message = $ws.Receive(2000)

                if ($null -ne $message) {
                    # Check for klippy state in notifications
                    if ($message.method -eq 'notify_klippy_ready') {
                        if (-not $Quiet) {
                            Write-Host " Ready!"
                        }

                        return [PSCustomObject]@{
                            PSTypeName  = 'KlippyCLI.WaitResult'
                            PrinterId   = $printer.Id
                            PrinterName = $printer.PrinterName
                            State       = 'ready'
                            WaitTime    = [datetime]::UtcNow - $startTime
                            Success     = $true
                        }
                    }

                    # Also check webhooks state updates
                    if ($message.method -eq 'notify_status_update' -and $message.params) {
                        $status = $message.params[0]
                        if ($status.webhooks.state -eq 'ready') {
                            if (-not $Quiet) {
                                Write-Host " Ready!"
                            }

                            return [PSCustomObject]@{
                                PSTypeName  = 'KlippyCLI.WaitResult'
                                PrinterId   = $printer.Id
                                PrinterName = $printer.PrinterName
                                State       = 'ready'
                                WaitTime    = [datetime]::UtcNow - $startTime
                                Success     = $true
                            }
                        }
                    }
                }

                if (-not $Quiet) {
                    Write-Host "." -NoNewline
                }

                # Periodically check via REST as backup
                try {
                    $currentInfo = Invoke-KlippyJsonRpc -Printer $printer -Method "printer/info"
                    if ($currentInfo.State -eq 'ready') {
                        if (-not $Quiet) {
                            Write-Host " Ready!"
                        }
                        return [PSCustomObject]@{
                            PSTypeName  = 'KlippyCLI.WaitResult'
                            PrinterId   = $printer.Id
                            PrinterName = $printer.PrinterName
                            State       = 'ready'
                            WaitTime    = [datetime]::UtcNow - $startTime
                            Success     = $true
                        }
                    }
                }
                catch {
                    # Ignore REST errors during wait
                }
            }

            # Timeout
            if (-not $Quiet) {
                Write-Host " Timeout!"
            }

            Write-Warning "[$($printer.PrinterName)] Timeout waiting for ready state after $Timeout seconds"

            return [PSCustomObject]@{
                PSTypeName  = 'KlippyCLI.WaitResult'
                PrinterId   = $printer.Id
                PrinterName = $printer.PrinterName
                State       = 'timeout'
                WaitTime    = [datetime]::UtcNow - $startTime
                Success     = $false
            }
        }
        catch {
            if (-not $Quiet) {
                Write-Host " Error!"
            }
            Write-Error "[$($printer.PrinterName)] Error waiting for ready state: $_"

            return [PSCustomObject]@{
                PSTypeName  = 'KlippyCLI.WaitResult'
                PrinterId   = $printer.Id
                PrinterName = $printer.PrinterName
                State       = 'error'
                WaitTime    = [datetime]::UtcNow - $startTime
                Success     = $false
                Error       = $_.Exception.Message
            }
        }
        finally {
            if ($ws) {
                $ws.Close()
            }
        }
    }
}