PrismShell.psm1

$script:ModuleRoot = $PSScriptRoot
$script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\PrismShell.psd1").ModuleVersion

# Detect whether at some level dotsourcing was enforced
$script:doDotSource = Get-PSFConfigValue -FullName PrismShell.Import.DoDotSource -Fallback $false
if ($PrismShell_dotsourcemodule) { $script:doDotSource = $true }

<#
Note on Resolve-Path:
All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator.
This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS.
Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist.
This is important when testing for paths.
#>


# Detect whether at some level loading individual module files, rather than the compiled module was enforced
$importIndividualFiles = Get-PSFConfigValue -FullName PrismShell.Import.IndividualFiles -Fallback $false
if ($PrismShell_importIndividualFiles) { $importIndividualFiles = $true }
if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true }
if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true }
    
function Import-ModuleFile
{
    <#
        .SYNOPSIS
            Loads files into the module on module import.
         
        .DESCRIPTION
            This helper function is used during module initialization.
            It should always be dotsourced itself, in order to proper function.
             
            This provides a central location to react to files being imported, if later desired
         
        .PARAMETER Path
            The path to the file to load
         
        .EXAMPLE
            PS C:\> . Import-ModuleFile -File $function.FullName
     
            Imports the file stored in $function according to import policy
    #>

    [CmdletBinding()]
    Param (
        [string]
        $Path
    )
    
    $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath
    if ($doDotSource) { . $resolvedPath }
    else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) }
}

#region Load individual files
if ($importIndividualFiles)
{
    # Execute Preimport actions
    . Import-ModuleFile -Path "$ModuleRoot\internal\scripts\preimport.ps1"
    
    # Import all internal functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Import all public functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Execute Postimport actions
    . Import-ModuleFile -Path "$ModuleRoot\internal\scripts\postimport.ps1"
    
    # End it here, do not load compiled code below
    return
}
#endregion Load individual files

#region Load compiled code
<#
This file loads the strings documents from the respective language folders.
This allows localizing messages and errors.
Load psd1 language files for each language you wish to support.
Partial translations are acceptable - when missing a current language message,
it will fallback to English or another available language.
#>

Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'PrismShell' -Language 'en-US'

<#
.SYNOPSIS
    Get ARP cache entries
.DESCRIPTION
    Get ARP cache entries
.PARAMETER ComputerName
    The host name to get the specific entry for
.EXAMPLE
    Get-ArpCache -ComputerName prism.janhendrikpeter.de
 
    Get the Prism in your domain...
#>

function Get-ArpCache
{
    param
    (
        [Parameter()]
        [string]
        $ComputerName
    )

    if (-not [string]::IsNullOrWhiteSpace($ComputerName))
    {
        try
        {
            $ipAddress = [System.Net.Dns]::GetHostByName($ComputerName).AddressList[0].IPAddressToString
        }
        catch
        {
            Stop-PSFFunction -EnableException $true -String GetArpCache.DnsLookupFailed -StringValues $ComputerName -Cmdlet $PSCmdlet -Exception $_.Exception
        }
    }

    $cacheText = & (Get-Command -Name arp).Path --% -a

    $cache = foreach ($entry in ($cacheText | Where-Object {$_ -match '^\s*\d'}))
    {
        Write-PSFMessage -String 'GetArpCache.AddingEntry' -StringValues $entry
        $ip, $mac, $type = $entry.Trim() -split '\s+'

        if ($mac -notmatch '^([a-fA-F0-9]{2}-){5}[a-fA-F0-9]{2}$')
        {
            Write-PSFMessage -String 'GetArpCache.MalformedMac' -StringValues $entry, $mac
            continue
        }

        [PSCustomObject]@{
            IPAddress    = $ip
            MACAddress   = ($mac -replace '-',':')
            Type         = $type
        }
    }

    if (-not [string]::IsNullOrWhiteSpace($ipAddress))
    {
        Write-PSFMessage -String 'GetArpCache.Filtering' -StringValues $ipAddress
        return ($cache | Where-Object IPAddress -eq $ipAddress)
    }

    $cache
}


class PrismPrinter
{
    [string] $IPAddress
    [string] $MacAddress
    [version] $FirmwareVersion

    PrismPrinter ()
    { }

    PrismPrinter ([string] $ipAddress, [string] $macAddress)
    {
        $this.IPAddress = $ipAddress
        $this.MacAddress = $macAddress
        $this.FirmwareVersion = [version]::new()
    }

    PrismPrinter ([string] $ipAddress, [string] $macAddress, [version] $version)
    {
        $this.IPAddress = $ipAddress
        $this.MacAddress = $macAddress
        $this.FirmwareVersion = $version
    }

    [string] ToString()
    {
        return $this.IPAddress
    }
}

class PrismProfile
{
    #{"Material":" ","BaseCureTime": ,"CureTime": ,"RaiseDistance": }
    [string] $Material

    [uint16] $BaseCureTime

    [uint16] $CureTime

    [uint16] $RaiseDistance

    PrismProfile ()
    {

    }

    PrismProfile ([string] $Material, [uint16] $BaseCureTime, [uint16] $CureTime, [uint16] $RaiseDistance)
    {
        $this.Material = $Material
        $this.BaseCureTime = $BaseCureTime
        $this.CureTime = $CureTime
        $this.RaiseDistance = $RaiseDistance
    }
}


<#
.SYNOPSIS
    Display toast message
.DESCRIPTION
    Display toast message
.PARAMETER Message
    The message to display
.EXAMPLE
    Show-PrismToast -Message ((Get-PSFLocalizedString -Module PrismShell -Name StartPrismPrint.PrintFinished) -f $ComputerName, $Name)
 
    Displays a toast message if the printer is done
#>

function Show-PrismToast
{
    param
    (
        [Parameter(Mandatory)]
        [string]
        $Message
    )

    if ($IsLinux -or $IsMacOS) {return}
    
    if ([environment]::OSVersion.Version -lt 6.3) { return }

    $toastProvider = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\WindowsPowerShell\v1.0\powershell.exe"
    $template = "<toast><visual><binding template=`"ToastText02`"><text id=`"1`">PrismShell</text><text id=`"2`">{0}</text></binding></visual></toast>" -f $Message


    try
    {
        [void]([Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime])
        [void]([Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime])
        [void]([Windows.UI.Notifications.ToastNotification, Windows.UI.Notifications, ContentType = WindowsRuntime])
        $xml = New-Object Windows.Data.Xml.Dom.XmlDocument

        $xml.LoadXml($template)
        $toast = New-Object Windows.UI.Notifications.ToastNotification $xml
        [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier($toastProvider).Show($toast)
    }
    catch
    {
        Write-PSFMessage "Error sending toast notification: $($_.Exception.Message)"
    }
}

<#
.SYNOPSIS
    Add a new resin profile
.DESCRIPTION
    CURRENTLY NOT WORKING
    This Cmdlet adds a resin profile to the index of available profiles (1-7).
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.PARAMETER Index
    The integer-based index between 1 and 7 to the resin profile
.PARAMETER PrismProfile
    The profile generated with New-PrismProfile
.PARAMETER Material
    Material, e.g. AwesomeStuff
.PARAMETER BaseCureTime
    Time in seconds for the base layer cure
.PARAMETER CureTime
    Time in seconds for each layer
.PARAMETER RaiseDistance
    The distance the build plate rises after each layer
.PARAMETER Force
    Overwrite existing profiles
.EXAMPLE
    Add-PrismProfile -Index 4 -Material GoodStuff -BaseCureTime 70 -CureTime 3 -RaiseDistance 5
 
    Adds a new profile to the default printer
#>

function Add-PrismProfile
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session,

        [Parameter(Mandatory)]
        [ValidateRange(1,7)]
        [uint16]
        $Index,

        [Parameter(Mandatory, ParameterSetName = 'ByProfile')]
        [PrismProfile]
        $PrismProfile,

        [Parameter(Mandatory, ParameterSetName = 'Direct')]
        [string]
        $Material,

        [Parameter(Mandatory, ParameterSetName = 'Direct')]
        [uint16]
        $BaseCureTime,

        [Parameter(Mandatory, ParameterSetName = 'Direct')]
        [uint16]
        $CureTime,

        [Parameter(Mandatory, ParameterSetName = 'Direct')]
        [uint16]
        $RaiseDistance,

        [switch]
        $Force
    )

    if (-not $Force.IsPresent)
    {
        Stop-PSFFunction -String AddPrismProfile.NotImplemented
    }

    $uri = "http://$ComputerName/resin{0}.conf" -f $Index

    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    if ($null -ne (Get-PrismProfile -ComputerName $ComputerName -Session $Session -Index $Index) -and -not $Force.IsPresent)
    {
        Stop-PSFFunction -String 'AddPrismProfile.ProfileExistsError' -StringValues $Index,$ComputerName
    }

    if ($null -eq $PrismProfile)
    {
        $PrismProfile = New-PrismProfile -Material $Material -BaseCureTime $BaseCureTime -CureTime $CureTime -RaiseDistance $RaiseDistance
    }

    # This needs some work. Printer acknowledges with OK, but doesn't do it
    $session.Headers.Add("Content-Length", $prismJson.Length)
    $session.Headers.Add("X-Requested-With", "XMLHttpRequest")
    $session.Headers.Add("Content-Type", "text/xml-external-parsed-entity")
    $session.Headers.Add("Accept-Encoding", "gzip, deflate")
    $session.Headers.Add("Accept-Language", "en-US,en;q=0.9")
    $null = Invoke-RestMethod -Uri $uri -WebSession $session -Body $PrismProfile.ToString() -Method Post
}


<#
.SYNOPSIS
    Remove the default printer
.DESCRIPTION
    Remove the default printer, e.g. to start using auto discovery
.EXAMPLE
    Clear-PrismDefaultPrinter
 
    Removes default host name and MAC address setting
#>

function Clear-PrismDefaultPrinter
{
    [CmdletBinding()]
    param ()

    Unregister-PSFConfig -Module PrismShell -Name DefaultPrinter.ComputerName
    Unregister-PSFConfig -Module PrismShell -Name DefaultPrinter.MacAddress
    Set-PSFConfig -Module PrismShell -Name DefaultPrinter.ComputerName -Value $null
    Set-PSFConfig -Module PrismShell -Name DefaultPrinter.MacAddress -Value $null
}


<#
.SYNOPSIS
    Disables auto-discovery
.DESCRIPTION
    Disables printer auto-discovery
.EXAMPLE
    Disable-PrismAutodiscovery
 
    Disables the auto discovery setting FOREVER
#>

function Disable-PrismAutodiscovery
{
    [CmdletBinding()]
    param ( )

    Set-PSFConfig -Module PrismShell -Name AutoDiscovery.Enabled -Value $false -PassThru | Register-PSFConfig
}


<#
.SYNOPSIS
    Enable printer auto-discovery
.DESCRIPTION
    Enable printer auto-discovery
.PARAMETER DoNotUseFirstPrinter
    If not specified, the first discovered printer will be used.
.EXAMPLE
    Enable-PrismAutodiscovery
 
    Configures two PSFramework settings to enable auto discovery FOREVER
#>

function Enable-PrismAutodiscovery
{
    [CmdletBinding()]
    param
    (
        [switch]
        $DoNotUseFirstPrinter
    )

    Set-PSFConfig -Module PrismShell -Name AutoDiscovery.Enabled -Value $true -PassThru | Register-PSFConfig
    Set-PSFConfig -Module PrismShell -Name AutoDiscovery.UseFirstPrinter -Value (-not $DoNotUseFirstPrinter.IsPresent) -PassThru | Register-PSFConfig
}


<#
.SYNOPSIS
    List all files on the SD card
.DESCRIPTION
    List all files on the SD card
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.PARAMETER Name
    Filter the file list by name. Returns $null if the file does not exist
.EXAMPLE
    Get-PrismFile -Name MAD5A.cddlp
 
    Try to locate your sliced Marauder II on your printer's SD card
#>

function Get-PrismFile
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session,

        [Parameter()]
        [string]
        $Name
    )

    $uri = "http://$ComputerName/filelist.json"

    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    Write-PSFMessage -String 'GetPrismFile.DiscoveryStarted' -StringValues $ComputerName
    [System.IO.FileInfo[]]$files = (Invoke-RestMethod -Uri $uri -WebSession $session).Text

    if (-not [string]::IsNullOrWhiteSpace($Name))
    {
        Write-PSFMessage -String 'GetPrismFile.FilterName' -StringValues $ComputerName, $Name
        return ($files | Where-Object Name -eq $Name)
    }

    $files
}


<#
.SYNOPSIS
    Discover all or the first/default printer
.DESCRIPTION
    Discover all or the first/default printer
.EXAMPLE
    Get-PrismPrinter
 
    Lists 0-n Prisms
#>

function Get-PrismPrinter
{
    [CmdletBinding()]
    param ( )

    if (-not (Get-PSFConfigValue -FullName PrismShell.AutoDiscovery.Enabled))
    {
        Write-PSFMessage -String 'FindPrismPrinter.AutoDiscoDisabled'
        return [pscustomobject]@{
            IPAddress = Get-PSFConfigValue -FullName PrismShell.DefaultPrinter.ComputerName
            MACAddress = Get-PSFConfigValue -FullName PrismShell.DefaultPrinter.MacAddress
            Type = 'UserDefault'
        }
    }

    Write-PSFMessage -String 'FindPrismPrinter.AutoDiscoEnabled'
    $filteredArpCache = Get-ArpCache | Where-Object MacAddress -like '10:00:F7*'

    if ($filteredArpCache.Count -eq 0)
    {
        Stop-PSFFunction -String 'FindPrismPrinter.HashtagSad'
    }

    if ($filteredArpCache.Count -gt 1 -and -not (Get-PSFConfigValue -FullName PrismShell.AutoDiscovery.UseFirstPrinter))
    {
        Write-PSFMessage -String 'FindPrismPrinter.MultiplePrintersFound' -Level Warning
    }

    if (Get-PSFConfigValue -FullName PrismShell.AutoDiscovery.UseFirstPrinter)
    {
        $filteredArpCache = $filteredArpCache | Select-Object -First 1
    }

    foreach ($entry in $filteredArpCache)
    {
        $uri = 'http://{0}/version' -f $entry.IPAddress
        $session = New-PrismSession -ComputerName $entry.IPAddress -MacAddress $entry.MacAddress.ToUpper()
        $version = (Invoke-RestMethod -Uri $uri -Method Get -WebSession $session) -as [version]

        if ($null -eq $version) {$version = [version]::new()}

        [PrismPrinter]::new($entry.IPAddress, $entry.MACAddress.ToUpper(), $version)
    }
}


<#
.SYNOPSIS
    List all profiles
.DESCRIPTION
    List all printer profiles, optionally filter by ID
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.PARAMETER Index
    Integer-based index between 1 and 7 that you want to retrieve
.EXAMPLE
    Get-PrismProfile
 
    List all profiles
#>

function Get-PrismProfile
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session,

        [Parameter()]
        [ValidateRange(1,7)]
        [uint16]
        $Index
    )

    $uri = "http://$ComputerName/user/{0}.conf"

    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    foreach ($i in (1..7))
    {
        if ($Index -and $i -ne $Index)
        {
            Write-PSFMessage -String 'GetPrismProfile.FilterIndex' -StringValues $ComputerName, $Index, $i
            continue
        }

        $profileData = Invoke-RestMethod -Uri ($uri -f $i) -Method Get -WebSession $Session
        if ($profileData -is [System.String]) { continue }

        $profileData
    }
}


<#
.SYNOPSIS
    Get printer settings
.DESCRIPTION
    Get all printer settings
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.EXAMPLE
    Get-PrismSetting
 
    name value
    ---- -----
    printerName replicator
    burnInLayers
    acceleration
    homeSpeed
    liftSpeed 5000
    levelingDelay
    clearTime
    ledPower
    mipiFlush
    zOffset
#>

function Get-PrismSetting
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session
    )

    $uri = "http://$ComputerName/setting"

    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    Invoke-RestMethod -Uri $uri -Method Get -WebSession $Session
}


<#
.SYNOPSIS
    Get the current status
.DESCRIPTION
    Get the current printer status
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.EXAMPLE
    Get-PrismStatus
 
    Gets current printer status
#>

function Get-PrismStatus
{
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session
    )

    $uri = "http://$ComputerName/status"

    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    $statusMessage, $file = (Invoke-RestMethod -Uri $uri -Method Get -WebSession $Session) -split '\s+'
    $status, $complete, $eta = $statusMessage -split ','

    [PSCustomObject]@{
        Status        = if ($status -eq 'P')
        {
            'Printing'
        }
        elseif ($status -eq 'L')
        {
            'Leveling'
        }
        elseif ($status -eq 'I')
        {
            'Idle'
        }
        else
        {
            'Unknown (printer status indicator was {0})' -f $status
        }
        Layer         = $complete
        TimeRemaining = $eta -as [timespan]
        FileName      = $file
    }
}


<#
.SYNOPSIS
    Return a new printer profile
.DESCRIPTION
    Create a new printer profile that can be used with Add-PrismProfile
.PARAMETER Material
    Material, e.g. AwesomeStuff
.PARAMETER BaseCureTime
    Time in seconds for the base layer cure
.PARAMETER CureTime
    Time in seconds for each layer
.PARAMETER RaiseDistance
    The distance the build plate rises after each layer
.EXAMPLE
    New-PrismProfile -Material GoodStuff -BaseCureTime 70 -CureTime 3 -RaiseDistance 5
 
    Generates a new profile object
#>

function New-PrismProfile
{
    param
    (
        [Parameter(Mandatory)]
        [string]
        $Material,

        [Parameter(Mandatory)]
        [uint16]
        $BaseCureTime,

        [Parameter(Mandatory)]
        [uint16]
        $CureTime,

        [Parameter(Mandatory)]
        [uint16]
        $RaiseDistance
    )

    [PrismProfile]::new($Material, $BaseCureTime, $CureTime, $RaiseDistance)
}


<#
.SYNOPSIS
    Create a new WebRequestSession with the cookie
.DESCRIPTION
    Creates a new WeqRequestSession with the printer's MAC address in the cookie which
    serves as authentication
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER MacAddress
    The mac address of the printer
.EXAMPLE
    New-PrismSession
 
    Create a new cookied-up web session to control your prism
#>

function New-PrismSession
{
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [string]
        $MacAddress
    )

    $physAddress = if ([string]::IsNullOrWhiteSpace($MacAddress))
    {
        try
        {
            (Get-ArpCache -ComputerName $ComputerName -ErrorAction Stop)[0].MacAddress.ToUpper()
        }
        catch
        {
            Stop-PSFFunction -String NewPrismSession.NoMacAddress -StringValues $ComputerName
        }
    }
    else
    {
        $MacAddress
    }

    Write-PSFMessage -String 'NewPrismSession.CreateAuthCookie' -StringValues $ComputerName, $physAddress
    $cookie = New-Object System.Net.Cookie
    $cookie.name = "Authorization"
    $cookie.path = "/"
    $cookie.value = $physAddress
    $cookie.domain = $ComputerName
    $cookie.expires = (Get-Date).AddDays(1)
    $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
    $session.cookies.add($cookie)
    $session
}


<#
.SYNOPSIS
    Remove a file on the SD card
.DESCRIPTION
    Remove a file on the SD card
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.PARAMETER Name
    The name of the file, can be piped from Get-PrismFile
.EXAMPLE
    Get-PrismFile -Name Daishi.cddlp | Remove-PrismFile
 
    Checks if your awesome Daishi BattleMech model exists, and if it does, removes it. Shame on you :(
    Unless you were making room for a Marauder...
#>

function Remove-PrismFile
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session,

        [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string]
        $Name
    )

    begin
    {
        $uri = "http://$ComputerName/CMD/Del/{0}"
        if ($null -eq $Session)
        {
            $Session = New-PrismSession -ComputerName $ComputerName
        }
    }

    process
    {
        Write-PSFMessage -String 'RemovePrismFile.Removing' -StringValues $ComputerName, $Name
        $null = Invoke-RestMethod -Uri ($uri -f $Name) -Method Get -WebSession $Session -ErrorAction Stop
    }
}


<#
.SYNOPSIS
    Resume a print on your Prism!
.DESCRIPTION
    Resume a print on your Prism!
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.EXAMPLE
    Resume-PrismPrint
 
    Resumes a paused print
#>

function Resume-PrismPrint
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session
    )

    $uri = "http://$ComputerName/CMD/Resume"
    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    if ((Get-PrismStatus -ComputerName $ComputerName -Session $Session).Status -in 'Idle','Unknown')
    {
        Write-PSFMessage -String 'ResumePrismPrint.NotResuming' -StringValues $ComputerName
    }

    Invoke-RestMethod -Uri $uri -Method Get -WebSession $Session
}


<#
.SYNOPSIS
    Set a default printer
.DESCRIPTION
    Set a default printer
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER MacAddress
    The mac address of the printer
.EXAMPLE
    Set-PrismDefaultPrinter -ComputerName 192.168.2.11 -MacAddress 11:22:33:44:55:66
 
    Sets a default printer for all other cmdlets
#>

function Set-PrismDefaultPrinter
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory)]
        [string]
        $ComputerName,

        [Parameter(Mandatory)]
        [string]
        $MacAddress
    )

    Set-PSFConfig -Module PrismShell -Name AutoDiscovery.Enabled -Value $false -PassThru | Register-PSFConfig
    Set-PSFConfig -Module PrismShell -Name DefaultPrinter.ComputerName -Value $ComputerName -PassThru | Register-PSFConfig
    Set-PSFConfig -Module PrismShell -Name DefaultPrinter.MacAddress -Value $MacAddress -PassThru | Register-PSFConfig
}


<#
.SYNOPSIS
    Change printer settings
.DESCRIPTION
    Change individual printer settings. Do this at your own peril! This cmdlet tries to validate
    your data, but certain settings might still damage your printer, e.g. increasing the LED power to
    the maximum allowed value of 140%
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.EXAMPLE
    Set-PrismSetting -PrinterName replicator01 -BurnInLayers 3
 
    Change the printer name and the number of burn-in layers
#>

function Set-PrismSetting
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session,

        # New printer name
        [Parameter()]
        [string]
        $PrinterName,

        # How many layers for burn-in should be used. Maximum 10
        [Parameter()]
        [ValidateRange(0, 10)]
        [nullable[uint16]]
        $BurnInLayers,

        # Maximum 10000
        [Parameter()]
        [ValidateRange(0, 10000)]
        [nullable[uint16]]
        $Acceleration,

        # Maximum 12000
        [Parameter()]
        [ValidateRange(0, 12000)]
        [nullable[uint16]]
        $HomeSpeed,

        # Maximum 15000
        [Parameter()]
        [ValidateRange(0, 15000)]
        [nullable[uint16]]
        $LiftSpeed,

        # Leveling delay in seconds
        [Parameter()]
        [nullable[uint16]]
        $LevelingDelaySecond,

        # Clear delay in seconds
        [Parameter()]
        [nullable[uint16]]
        $ClearDelaySecond,

        # LED power, min 20%, max 140%
        [Parameter()]
        [ValidateRange(20, 140)]
        [nullable[uint16]]
        $LedPowerPercent,

        # MIPI flush, max 1000
        [Parameter()]
        [ValidateRange(0, 1000)]
        [nullable[uint16]]
        $MipiFlush,

        # Z Offset in µm
        [Parameter()]
        [ValidateRange(0, 500)]
        [nullable[uint16]]
        $ZOffset
    )

    $uri = "http://$ComputerName/setting"

    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    $body = @{
        printerName   = $PrinterName
        burnInLayers  = $BurnInLayers
        acceleration  = $Acceleration
        homeSpeed     = $HomeSpeed
        liftSpeed     = $LiftSpeed
        levelingDelay = $LevelingDelaySecond
        clearTime     = $ClearDelaySecond
        ledPower      = $LedPowerPercent
        mipiFlush     = $MipiFlush
        zOffset       = $ZOffset
    }

    Write-PSFMessage -String 'SetPrismSetting.SettingApplied' -StringValues $ComputerName, ($body | Out-String)
    Invoke-RestMethod -Method Post -Uri $uri -WebSession $Session -Body ($body | ConvertTo-Json) -ContentType application/json
}


<#
.SYNOPSIS
    Start a print on your Prism!
.DESCRIPTION
    Starts to print a file that can be retrieved with Get-PrismFile
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.PARAMETER Name
    The file name to print
.PARAMETER Index
    The resin profile to print with, e.g. Get-PrismProfile
.PARAMETER Wait
    Indicates that the cmdlet will wait until the print is finished.
.PARAMETER AsJob
    Returns a Job object that waits in the background for the print to finish, or until you close PowerShell
.EXAMPLE
    Start-PrismPrint -Index 2 -Name MAD5A.cddlp
 
    Printer your awesome Marauder with your second profile
#>

function Start-PrismPrint
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session,

        [Parameter(Mandatory)]
        [string]
        $Name,

        [Parameter(Mandatory)]
        [ValidateRange(1, 7)]
        [uint16]
        $Index,

        [Parameter(ParameterSetName = 'Wait')]
        [switch]
        $Wait,

        [Parameter(ParameterSetName = 'WaitJob')]
        [switch]
        $AsJob
    )

    $uri = "http://$ComputerName/CMD/Print{0}{1}"
    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    if ($null -eq (Get-PrismProfile -ComputerName $ComputerName -Session $Session -Index $Index))
    {
        Stop-PSFFunction -String 'StartPrismPrint.ProfileMissing' -StringValues $ComputerName, $Index
    }

    if ($null -eq (Get-PrismFile -ComputerName $ComputerName -Session $Session -Name $Name))
    {
        Stop-PSFFunction -String 'StartPrismPrint.FileNotFound' -StringValues $ComputerName, $Name, $((Get-PrismFile -ComputerName $ComputerName -Session $Session).Name -join ',')
    }

    Write-PSFMessage -String 'StartPrismPrint.Starting' -StringValues $ComputerName, $Name, $Index
    $null = Invoke-RestMethod -Uri ($uri -f $Index, $Name) -Method Get -WebSession $Session

    if ($Wait.IsPresent)
    {
        while ((Get-PrismStatus).Status -in 'Leveling','Printing')
        {
            Start-Sleep -Seconds 30
            Write-PSFMessage -String StartPrismPrint.WaitingForGodot -StringValues $ComputerName, $Name
        }

        Show-PrismToast -Message ((Get-PSFLocalizedString -Module PrismShell -Name StartPrismPrint.PrintFinished) -f $ComputerName, $Name)
    }

    if ($AsJob.IsPresent)
    {
        Start-Job -Name ('{0}_{1}' -f $ComputerName, $Name) -ArgumentList $ComputerName,(Get-PrismPrinter).MacAddress -ScriptBlock {
            param($IP, $MAC)

            $printerSession = New-PrismSession -ComputerName $IP -MacAddress $MAC

            while ((Get-PrismStatus -ComputerName $IP -Session $printerSession).Status -in 'Leveling','Printing')
            {
                Start-Sleep -Seconds 30
                Write-PSFMessage -String StartPrismPrint.WaitingForGodot -StringValues $ComputerName, $Name
            }

            Show-PrismToast -Message ((Get-PSFLocalizedString -Module PrismShell -Name StartPrismPrint.PrintFinished) -f $ComputerName, $Name)
        }
    }
}


<#
.SYNOPSIS
    Stop a print on your Prism!
.DESCRIPTION
    Stop a print on your Prism!
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.EXAMPLE
    Stop-PrismPrint
 
    Cancels the current print on the default printer
#>

function Stop-PrismPrint
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session
    )

    $uri = "http://$ComputerName/CMD/Cancel"
    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    if ((Get-PrismStatus -ComputerName $ComputerName -Session $Session).Status -in 'Idle','Unknown')
    {
        Stop-PSFFunction -String 'StopPrismPrint.NotStopping' -StringValues $ComputerName
    }

    Write-PSFMessage -String 'StopPrismPrint.AttemptStop' -StringValues $ComputerName
    Invoke-RestMethod -Uri $uri -Method Get -WebSession $Session
}


<#
.SYNOPSIS
    Pause a print on your Prism!
.DESCRIPTION
    Pause a print on your Prism!
.PARAMETER ComputerName
    The host name or IP of your Prism
.PARAMETER Session
    The session to your Prism, autocreated if not provided
.EXAMPLE
    Suspend-PrismPrint
 
    Pauses a running print
#>

function Suspend-PrismPrint
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [string]
        $ComputerName = (Get-PrismPrinter).IPAddress,

        [Parameter()]
        [microsoft.powershell.commands.webrequestsession]
        $Session
    )

    $uri = "http://$ComputerName/CMD/Pause"
    if ($null -eq $Session)
    {
        $Session = New-PrismSession -ComputerName $ComputerName
    }

    if ((Get-PrismStatus -ComputerName $ComputerName -Session $Session).Status -in 'Idle','Unknown')
    {
        Stop-PSFFunction -String 'SuspendPrismPrint.NotSuspending' -StringValues $ComputerName
    }

    Write-PSFMessage -String 'SuspendPrismPrint.AttemptPause' -StringValues $ComputerName
    Invoke-RestMethod -Uri $uri -Method Get -WebSession $Session
}


# Configure validation
Register-PSFConfigValidation -Name MacAddressColon -ScriptBlock {
    param
    (
        [string]
        $MacAddress
    )

    if ([string]::IsNullOrWhiteSpace($MacAddress))
    {
        return [PSCustomObject]@{
            Message = 'Null-value supplied, but allowed'
            Success = $true
            Value = $null
        }
    }

    $res = $MacAddress -match '^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$'

    if ($res)
    {
        [PSCustomObject]@{
            Message = '{0} is a valid colon-separated MAC address' -f $MacAddress
            Success = $true
            Value = $MacAddress
        }
    }
    else
    {
        [PSCustomObject]@{
            Message = '{0} is not a valid colon-separated MAC address' -f $MacAddress
            Success = $false
            Value = $matchedValue
        }
    }
}

Set-PSFConfig -Module 'PrismShell' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging."
Set-PSFConfig -Module 'PrismShell' -Name 'Import.IndividualFiles' -Value $true -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments."

# Printer config
Set-PSFConfig -Module 'PrismShell' -Name 'AutoDiscovery.Enabled' -Value $true -Initialize -Validation 'bool' -Description 'Whether or not AutoDiscovery is enabled'
Set-PSFConfig -Module 'PrismShell' -Name 'AutoDiscovery.UseFirstPrinter' -Value $true -Initialize -Validation 'bool' -Description 'Whether or not the first discovered printer is enabled'

# Default printer
Set-PSFConfig -Module 'PrismShell' -Name 'DefaultPrinter.ComputerName' -Description 'If auto discovery is disabled, use this setting to configure the default IP or DNS Host Name of your printer.'
Set-PSFConfig -Module 'PrismShell' -Name 'DefaultPrinter.MacAddress' -Description 'The MAC address of your printer. If not specified and auto discovery is disabled, the ARP table will be queried for the MAC address' -Validation macaddresscolon


<#
Stored scriptblocks are available in [PsfValidateScript()] attributes.
This makes it easier to centrally provide the same scriptblock multiple times,
without having to maintain it in separate locations.
 
It also prevents lengthy validation scriptblocks from making your parameter block
hard to read.
 
Set-PSFScriptblock -Name 'PrismShell.ScriptBlockName' -Scriptblock {
     
}
#>


<#
# Example:
Register-PSFTeppScriptblock -Name "PrismShell.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' }
#>


Register-PSFTeppScriptblock -Name 'PrismShell.Printers' -ScriptBlock { (Get-PrismPrinter).IpAddress }


<#
# Example:
Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name PrismShell.alcohol
#>


Register-PSFTeppArgumentCompleter -Command Add-PrismProfile, Get-PrismFile, Get-PrismProfile, Get-PrismStatus, New-PrismSession, Remove-PrismFile, Resume-PrismPrint, Stop-PrismPrint, Suspend-PrismPrint -Parameter ComputerName -Name 'PrismShell.Printers'


New-PSFLicense -Product 'PrismShell' -Manufacturer 'janhe' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2019-11-18") -Text @"
Copyright (c) 2019 janhe
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@

#endregion Load compiled code