functions/public/Get-KlippyMacro.ps1

function Get-KlippyMacro {
    <#
    .SYNOPSIS
        Gets G-code macros from a Klipper printer.

    .DESCRIPTION
        Retrieves all G-code macros configured on the printer,
        including their description, variables, and parameters.

    .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 macro name. Supports wildcards.

    .PARAMETER IncludeConfig
        Include the full macro configuration/definition.

    .EXAMPLE
        Get-KlippyMacro
        Gets all macros from the default printer.

    .EXAMPLE
        Get-KlippyMacro -PrinterName "voronv2"
        Gets all macros from the specified printer.

    .EXAMPLE
        Get-KlippyMacro -Name "PRINT_*"
        Gets macros starting with "PRINT_".

    .EXAMPLE
        Get-KlippyMacro -Name "G32" -IncludeConfig
        Gets the G32 macro with its full configuration.

    .OUTPUTS
        KlippyCLI.Macro 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()]
        [switch]$IncludeConfig
    )

    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"

            # Find gcode_macro objects
            $macroObjects = foreach ($obj in $allObjects.Objects) {
                if ($obj.StartsWith('gcode_macro ')) {
                    $macroName = ($obj -split ' ', 2)[1]
                    [PSCustomObject]@{
                        ObjectName = $obj
                        MacroName  = $macroName
                    }
                }
            }

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

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

            # Get G-code help for descriptions
            $gcodeHelp = @{}
            try {
                $helpResponse = Invoke-KlippyJsonRpc -Printer $printer -Method "printer/gcode/help" -NoNormalize
                foreach ($prop in $helpResponse.PSObject.Properties) {
                    $gcodeHelp[$prop.Name.ToUpper()] = $prop.Value
                }
            }
            catch {
                Write-Verbose "Could not fetch G-code help: $_"
            }

            # Get configfile for full macro definitions if requested
            $configData = $null
            if ($IncludeConfig) {
                try {
                    $configResponse = Invoke-KlippyJsonRpc -Printer $printer -Method "printer/objects/query?configfile" -NoNormalize
                    $configData = $configResponse.status.configfile.settings
                }
                catch {
                    Write-Verbose "Could not fetch config data: $_"
                }
            }

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

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

            # Process results
            foreach ($macro in $macroObjects) {
                $data = $response.status.($macro.ObjectName)
                $macroNameUpper = $macro.MacroName.ToUpper()

                # Get description from gcode help
                $description = $gcodeHelp[$macroNameUpper]

                # Parse parameters from description if available
                $parameters = @()
                if ($description -match 'params?:?\s*(.+)$') {
                    $paramStr = $Matches[1]
                    # Try to extract parameter names
                    $paramMatches = [regex]::Matches($paramStr, '\b([A-Z_][A-Z0-9_]*)\b')
                    $parameters = $paramMatches | ForEach-Object { $_.Groups[1].Value } | Select-Object -Unique
                }

                # Get variables from the macro object
                $variables = @{}
                if ($data) {
                    foreach ($prop in $data.PSObject.Properties) {
                        $variables[$prop.Name] = $prop.Value
                    }
                }

                $result = [PSCustomObject]@{
                    PSTypeName  = 'KlippyCLI.Macro'
                    PrinterId   = $printer.Id
                    PrinterName = $printer.PrinterName
                    Name        = $macro.MacroName
                    Description = $description
                    Variables   = if ($variables.Count -gt 0) { $variables } else { $null }
                }

                # Add config data if requested
                if ($IncludeConfig -and $configData) {
                    $configKey = "gcode_macro $($macro.MacroName.ToLower())"
                    $macroConfig = $configData.$configKey
                    if ($macroConfig) {
                        # Extract parameters from gcode definition
                        $gcode = $macroConfig.gcode
                        if ($gcode) {
                            # Find params.XXXX references
                            $paramRefs = [regex]::Matches($gcode, 'params\.([A-Za-z_][A-Za-z0-9_]*)', 'IgnoreCase')
                            $foundParams = $paramRefs | ForEach-Object { $_.Groups[1].Value.ToUpper() } | Select-Object -Unique
                            if ($foundParams) {
                                $parameters = $foundParams
                            }
                        }

                        $result | Add-Member -NotePropertyName 'Gcode' -NotePropertyValue $macroConfig.gcode -Force
                        $result | Add-Member -NotePropertyName 'RenameExisting' -NotePropertyValue $macroConfig.rename_existing -Force

                        # Extract default parameter values
                        $defaultParams = @{}
                        foreach ($prop in $macroConfig.PSObject.Properties) {
                            if ($prop.Name.StartsWith('default_parameter_')) {
                                $paramName = $prop.Name.Replace('default_parameter_', '').ToUpper()
                                $defaultParams[$paramName] = $prop.Value
                            }
                        }
                        if ($defaultParams.Count -gt 0) {
                            $result | Add-Member -NotePropertyName 'DefaultParameters' -NotePropertyValue $defaultParams -Force
                        }
                    }
                }

                # Add parameters if found
                if ($parameters -and $parameters.Count -gt 0) {
                    $result | Add-Member -NotePropertyName 'Parameters' -NotePropertyValue $parameters -Force
                }

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