functions/private/Invoke-KlippyJsonRpc.ps1
|
function Invoke-KlippyJsonRpc { <# .SYNOPSIS Invokes a JSON-RPC method on a Moonraker instance. .DESCRIPTION Sends a JSON-RPC 2.0 request to Moonraker and returns the result. Supports API key authentication, timeouts, and retries. Optionally normalizes response property names to PascalCase. .PARAMETER Printer The printer object (from Get-KlippyPrinter or Resolve-KlippyPrinterTarget). .PARAMETER Method The JSON-RPC method name (e.g., "printer.info", "server.info"). .PARAMETER Params Optional parameters hashtable for the method. .PARAMETER Timeout Request timeout in seconds. Default is 30. .PARAMETER RetryCount Number of retries on failure. Default is 0. .PARAMETER RetryDelay Delay between retries in seconds. Default is 2. .PARAMETER NoNormalize Skip PascalCase normalization of response. .PARAMETER RawResponse Return the full response including jsonrpc envelope. .EXAMPLE $printer = Get-KlippyPrinter -PrinterName "voronv2" Invoke-KlippyJsonRpc -Printer $printer -Method "printer.info" .OUTPUTS PSCustomObject - The result from the JSON-RPC call. #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true)] [PSCustomObject]$Printer, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Method, [Parameter()] [hashtable]$Params, [Parameter()] [ValidateRange(1, 600)] [int]$Timeout = 30, [Parameter()] [ValidateRange(0, 10)] [int]$RetryCount = 0, [Parameter()] [ValidateRange(1, 60)] [int]$RetryDelay = 2, [Parameter()] [switch]$NoNormalize, [Parameter()] [switch]$RawResponse ) # Build the JSON-RPC request $requestId = [System.Random]::new().Next(1, 999999) $rpcRequest = @{ jsonrpc = "2.0" method = $Method id = $requestId } if ($Params -and $Params.Count -gt 0) { $rpcRequest['params'] = $Params } $jsonBody = $rpcRequest | ConvertTo-Json -Depth 10 -Compress # Build headers $headers = @{ 'Content-Type' = 'application/json' } if ($Printer.ApiKey) { $headers['X-Api-Key'] = $Printer.ApiKey } # Build URI $baseUri = $Printer.Uri.TrimEnd('/') $uri = "$baseUri/printer/objects/query" # For non-query methods, use the appropriate endpoint # Moonraker accepts JSON-RPC at multiple endpoints or REST-style calls # We'll use REST-style for simplicity as it's more widely supported $uri = "$baseUri/api/$($Method -replace '\.', '/')" # Actually, let's use the direct endpoint mapping that Moonraker provides # Most methods map to: /method/path where method.path becomes /method/path $methodPath = $Method -replace '\.', '/' $uri = "$baseUri/$methodPath" $attempt = 0 $maxAttempts = $RetryCount + 1 $lastError = $null while ($attempt -lt $maxAttempts) { $attempt++ try { Write-Verbose "[$($Printer.PrinterName)] Invoking $Method (attempt $attempt/$maxAttempts)" Write-Verbose "URI: $uri" $invokeParams = @{ Uri = $uri Method = 'GET' Headers = $headers TimeoutSec = $Timeout ErrorAction = 'Stop' } # Use POST with body if we have params if ($Params -and $Params.Count -gt 0) { # For GET requests, convert params to query string $queryParams = @() foreach ($key in $Params.Keys) { $value = $Params[$key] if ($value -is [array]) { $value = $value -join ',' } $queryParams += "$key=$([System.Uri]::EscapeDataString($value.ToString()))" } if ($queryParams.Count -gt 0) { $uri = "$uri`?$($queryParams -join '&')" $invokeParams['Uri'] = $uri } } $response = Invoke-RestMethod @invokeParams # Handle Moonraker response format if ($RawResponse) { return $response } # Extract result (Moonraker wraps responses in {"result": ...}) $result = if ($response.result) { $response.result } else { $response } # Normalize to PascalCase unless disabled if (-not $NoNormalize) { $result = ConvertTo-KlippyPascalCaseObject -InputObject $result } # Add printer context if ($result -is [PSCustomObject]) { $result | Add-Member -NotePropertyName '_PrinterId' -NotePropertyValue $Printer.Id -Force $result | Add-Member -NotePropertyName '_PrinterName' -NotePropertyValue $Printer.PrinterName -Force } return $result } catch { $lastError = $_ Write-Verbose "[$($Printer.PrinterName)] Request failed: $($_.Exception.Message)" if ($attempt -lt $maxAttempts) { Write-Verbose "Retrying in $RetryDelay seconds..." Start-Sleep -Seconds $RetryDelay } } } # All retries exhausted $errorMessage = "Failed to invoke '$Method' on '$($Printer.PrinterName)' after $maxAttempts attempt(s): $($lastError.Exception.Message)" # Try to extract Moonraker error details if ($lastError.ErrorDetails.Message) { try { $errorDetails = $lastError.ErrorDetails.Message | ConvertFrom-Json if ($errorDetails.error.message) { $errorMessage = "[$($Printer.PrinterName)] $($errorDetails.error.message)" } } catch { # Ignore JSON parsing errors } } throw $errorMessage } |