HP.ClientManagement.psm1

#
# Copyright 2018-2024 HP Development Company, L.P.
# All Rights Reserved.
#
# NOTICE: All information contained herein is, and remains the property of HP Development Company, L.P.
#
# The intellectual and technical concepts contained herein are proprietary to HP Development Company, L.P
# and may be covered by U.S. and Foreign Patents, patents in process, and are protected by
# trade secret or copyright law. Dissemination of this information or reproduction of this material
# is strictly forbidden unless prior written permission is obtained from HP Development Company, L.P.

using namespace HP.CMSLHelper

Add-Type -AssemblyName System.Web
Set-StrictMode -Version 3.0
#requires -Modules "HP.Private"

# CMSL is normally installed in C:\Program Files\WindowsPowerShell\Modules
# but if installed via PSGallery and via PS7, it is installed in a different location
if (Test-Path "$PSScriptRoot\..\HP.Private\HP.CMSLHelper.dll") {
  Add-Type -Path "$PSScriptRoot\..\HP.Private\HP.CMSLHelper.dll"
}
else{
  Add-Type -Path "$PSScriptRoot\..\..\HP.Private\1.8.0\HP.CMSLHelper.dll"
}

<#
.SYNOPSIS
  Retrieves an HP BIOS Setting object by name on the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves an HP BIOS Setting object by name on the current device unless specified for another platform.
 
.PARAMETER Name
  Specifies the name of the BIOS setting to retrieve. This parameter is mandatory and has no default value.
 
.PARAMETER Format
  Specifies the format of the result. The value must be one of the following values:
 
    - BCU: format as HP BIOS Config Utility input format
    - CSV: format as a comma-separated values list
    - XML: format as XML
    - JSON: format as JSON
 
  If not specified, the default PowerShell formatting is used.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
.NOTES
  HP BIOS is required.
 
.EXAMPLE
  Get-HPBIOSSetting -Name "Serial Number" -Format BCU
#>

function Get-HPBIOSSetting {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetting")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $true)]
    $Name,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $false)]
    [ValidateSet('XML','JSON','BCU','CSV')]
    $Format,
    [Parameter(ParameterSetName = 'NewSession',Position = 2,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 3,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $ns = getNamespace
  Write-Verbose "Reading HP BIOS Setting '$Name' from $ns on '$ComputerName'"
  $result = $null

  $params = @{
    Class = "HP_BIOSSetting"
    Namespace = $ns
    Filter = "Name='$name'"
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') {
    $params.CimSession = newCimSession -Target $ComputerName
  }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') {
    $params.CimSession = $CimSession
  }

  try {
    $result = Get-CimInstance @params -ErrorAction stop
  } catch [Microsoft.Management.Infrastructure.CimException]
  {
    if ($_.Exception.Message.trim() -eq "Access denied")
    {
      throw [System.UnauthorizedAccessException]"Access denied: Please ensure you have the rights to perform this operation."
    }
    throw [System.NotSupportedException]"$($_.Exception.Message): Please ensure this is a supported HP device."
  }


  if (-not $result) {
    $Err = "Setting not found: '" + $name + "'"
    throw [System.Management.Automation.ItemNotFoundException]$Err
  }
  Add-Member -InputObject $result -Force -NotePropertyName "Class" -NotePropertyValue $result.CimClass.CimClassName | Out-Null
  Write-Verbose "Retrieved HP BIOS Setting '$name' ok."

  switch ($format) {
    { $_ -eq 'CSV' } { return convertSettingToCSV ($result) }
    { $_ -eq 'XML' } { return convertSettingToXML ($result) }
    { $_ -eq 'BCU' } { return convertSettingToBCU ($result) }
    { $_ -eq 'JSON' } { return convertSettingToJSON ($result) }
    default { return $result }
  }
}


<#
 .SYNOPSIS
  Retrieves the device UUID via standard OS providers on the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the system UUID via standard OS providers. The result should normally match the result from the Get-HPBIOSUUID command.
 
.PARAMETER ComputerName
 Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
.EXAMPLE
  Get-HPDeviceUUID
#>

function Get-HPDeviceUUID () {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceUUID")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystemProduct'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "UUID")).trim().ToUpper()
}


<#
 .SYNOPSIS
  Retrieves the BIOS UUID on the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the system UUID from the BIOS. The result should normally match the result from the Get-HPDeviceUUID command.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPBIOSUUID
#>

function Get-HPBIOSUUID {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSUUID")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $params = @{ Name = 'Universally Unique Identifier (UUID)' }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params -ErrorAction stop
  if ($obj.Value -match '-') {
    return (getFormattedBiosSettingValue $obj)
  }
  else {
    $raw = ([guid]::new($obj.Value)).ToByteArray()
    $raw[0],$raw[3] = $raw[3],$raw[0]
    $raw[1],$raw[2] = $raw[2],$raw[1]
    $raw[4],$raw[5] = $raw[5],$raw[4]
    $raw[6],$raw[7] = $raw[7],$raw[6]
    return ([guid]::new($raw)).ToString().ToUpper().trim()
  }
}


<#
.SYNOPSIS
  Retrieves the current BIOS version on the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the current BIOS version. If the BIOS family is available and the -includeFamily parameter is specified, the BIOS family is also included in the result.
 
.PARAMETER IncludeFamily
  If specified, the BIOS family is included in the result.
 
.PARAMETER ComputerName
 Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPBIOSVersion
#>

function Get-HPBIOSVersion {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSVersion")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $false)]
    [switch]$IncludeFamily,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(Position = 1,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  $verfield = getWmiField $obj "SMBIOSBIOSVersion"
  $ver = $null

  Write-Verbose "Received object with $verfield"
  try {
    $ver = extractBIOSVersion $verfield
  }
  catch { throw [System.InvalidOperationException]"The BIOS version on this system could not be parsed. This BIOS may not be supported." }
  if ($includeFamily.IsPresent) { $result = $ver + " " + $verfield.Split()[0] }
  else { $result = $ver }
  $result.TrimStart("0").trim()
}

<#
.SYNOPSIS
  Retrieves the current BIOS author (manufacturer) on the current device unless specified for another platform
 
 
.DESCRIPTION
  This command retrieves the BIOS manufacturer via the Win32_BIOS WMI class. In some cases, the BIOS manufacturer may be different from the device manufacturer.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPBIOSAuthor
#>

function Get-HPBIOSAuthor {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSAuthor")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "Manufacturer")).trim()

}

<#
.SYNOPSIS
  Retrieves the current device manufacturer on the current device unless specified for another platform
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.DESCRIPTION
  This command retrieves the current device manufacturer on the current device unless specified for another platform via Windows Management Instrumentation (WMI). In some cases, the BIOS manufacturer may be different from the device manufacturer.
 
.EXAMPLE
  Get-HPDeviceManufacturer
#>

function Get-HPDeviceManufacturer {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceManufacturer")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystem'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "Manufacturer")).trim()
}

<#
.SYNOPSIS
  Retrieves the serial number on the current device unless specified for another platform
 
.DESCRIPTION
This command retrieves the serial number on the current device unless specified for another platform via Windows Management Instrumentation (WMI). This command is equivalent to reading the SerialNumber property from the Win32_BIOS WMI class. If no parameters are specified, this command will create its own one-time-use CIMSession object using the current device and default the CIMSession to use DCOM protocol.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPDeviceSerialNumber
#>

function Get-HPDeviceSerialNumber {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceSerialNumber")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop

  ([string](getWmiField $obj "SerialNumber")).trim()
}

<#
.SYNOPSIS
  Retrieves the official marketing name of the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the official marketing name of the current device unless specified for another platform.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPDeviceModel
#>

function Get-HPDeviceModel {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceModel")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystem'
    Namespace = 'root\cimv2'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop

  ([string](getWmiField $obj "Model")).trim()
}




<#
.SYNOPSIS
  Retrieves the Part Number (or SKU) on the current device unless specified for another platform
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
 Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.DESCRIPTION
  This command retrieves the Part Number (or SKU) on the current device unless specified for another platform. This command is equivalent to reading the field SystemSKUNumber from the WMI class Win32_ComputerSystem.
 
.EXAMPLE
  Get-HPDevicePartNumber
#>

function Get-HPDevicePartNumber {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDevicePartNumber")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_ComputerSystem'
    Namespace = 'root\cimv2'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }
  $obj = Get-CimInstance @params -ErrorAction stop

  ([string](getWmiField $obj "SystemSKUNumber")).trim().ToUpper()
}


<#
.SYNOPSIS
  Retrieves the product ID of the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the product ID of the current device unless specified for another platform. The product ID (Platform ID) is a 4-character hexadecimal string. It corresponds to the Product field in the Win32_BaseBoard WMI class.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPDeviceProductID
#>

function Get-HPDeviceProductID {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceProductID")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_BaseBoard'
    Namespace = 'root\cimv2'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-CimInstance @params -ErrorAction stop
  ([string](getWmiField $obj "Product")).trim().ToUpper()
}


<#
.SYNOPSIS
  Retrieves the device asset tag of the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the asset tag (also called the Asset Tracking Number) for a device. Some computers may have a blank asset tag or have the asset tag pre-populated with the serial number.
 
.PARAMETER ComputerName
 Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPDeviceAssetTag
#>

function Get-HPDeviceAssetTag {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceAssetTag")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $params = @{
    Name = 'Asset Tracking Number'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params -ErrorAction stop
  getFormattedBiosSettingValue $obj
}


<#
.SYNOPSIS
  Retrieves the value of a BIOS setting on the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the value of a BIOS setting on the current device unless specified for another platform. In comparison to the Get-HPBIOSSetting command that retrieves all fields for the BIOS setting, this command retrieves only the setting's value.
 
.NOTES
  HP BIOS is required.
 
.PARAMETER name
  Specifies the name of the BIOS setting to retrieve
 
.PARAMETER ComputerName
 Specifies a target computer to execute this command. If not specified, this command is executed on the local computer. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
 Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPBIOSSettingValue -Name 'Asset Tracking Number'
#>

function Get-HPBIOSSettingValue {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSettingValue")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $true)]
    [string]$Name,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $false)]
    [CimSession]$CimSession
  )
  $params = @{
    Name = $Name
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params
  if ($obj) {
    getFormattedBiosSettingValue $obj
  }


}


<#
.SYNOPSIS
  Retrieves all BIOS settings on the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves all BIOS settings on the current device unless specified for another platform as native objects or in a specified format.
 
.PARAMETER ComputerName
 Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER Format
  Specifies the format of the result. The value must be one of the following values:
 
    - BCU: format as HP BIOS Config Utility input format
    - CSV: format as a comma-separated values list
    - XML: format as XML
    - JSON: format as JSON
    - brief: (default) format as a list of names
 
  If not specified, the default PowerShell formatting is used.
 
.PARAMETER NoReadonly
  If specified, this command will not include read-only settings in the result.
 
.PARAMETER CimSession
 Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Get-HPBIOSSettingsList -Format BCU
 
.NOTES
  - Although this command supports HP BIOS Config Utility (BCU), note that redirecting the command's output to a file will not be usable by BCU, because PowerShell will insert a unicode BOM in the file. To obtain a compatible file, either remove the BOM manually or use bios-cli.ps1.
  - BIOS settings of type 'password' are not outputted when using XML, JSON, BCU, or CSV formats.
  - By convention, when representing multiple values in an enumeration as a single string, the value with an asterisk in front is the currently active value. For example, given the string "One,*Two,Three" representing three possible enumeration choices, the current active value is "Two".
  - Requires HP BIOS.
#>

function Get-HPBIOSSettingsList {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSettingsList")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $false)]
    [Parameter(Position = 0,Mandatory = $false)]
    [ValidateSet('XML','JSON','BCU','CSV','brief')]
    [string]$Format,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $false)]
    [Parameter(Position = 1,Mandatory = $false)] [switch]$NoReadonly,
    [Parameter(ParameterSetName = 'NewSession',Position = 2,Mandatory = $false)]
    [Alias('Target')]
    [Parameter(Position = 2,Mandatory = $false)] [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 3,Mandatory = $false)]
    [Parameter(Position = 3,Mandatory = $false)] [CimSession]$CimSession
  )
  $ns = getNamespace

  Write-Verbose "Getting all BIOS settings from '$ComputerName'"
  $params = @{
    ClassName = 'HP_BIOSSetting'
    Namespace = $ns
  }

  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  try {
    $cs = Get-CimInstance @params -ErrorAction stop
  }
  catch [Microsoft.Management.Infrastructure.CimException]{
    if ($_.Exception.Message.trim() -eq "Access denied")
    {
      throw [System.UnauthorizedAccessException]"Access denied: Please ensure you have the rights to perform this operation."
    }
    throw [System.NotSupportedException]"$($_.Exception.Message): Please ensure this is a supported HP device."
  }

  switch ($format) {
    { $_ -eq 'BCU' } {
      # to BCU format
      $now = Get-Date
      Write-Output "BIOSConfig 1.0"
      Write-Output ";"
      Write-Output "; Created by CMSL function Get-HPBIOSSettingsList"
      Write-Output "; Date=$now"
      Write-Output ";"
      Write-Output "; Found $($cs.count) settings"
      Write-Output ";"
      foreach ($c in $cs) {
        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            convertSettingToBCU ($c)
          }
        }
      }
      return
    }

    { $_ -eq 'XML' } {
      # to IA format
      Write-Output "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes"" ?>"
      Write-Output "<ImagePal>"
      Write-Output " <BIOSSettings>"

      foreach ($c in $cs) {
        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            convertSettingToXML ($c)
          }
        }
      }
      Write-Output " </BIOSSettings>"
      Write-Output "</ImagePal>"
      return
    }

    { $_ -eq 'JSON' } {
      # to JSON format
      $first = $true
      "[" | Write-Output


      foreach ($c in $cs) {
        Add-Member -InputObject $c -Force -NotePropertyName "Class" -NotePropertyValue $c.CimClass.CimClassName | Out-Null

        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            if ($first -ne $true) {
              Write-Output ","
            }
            convertSettingToJSON ($c)
            $first = $false
          }
        }

      }
      "]" | Write-Output

    }

    { $_ -eq 'CSV' } {
      # to CSV format
      Write-Output ("NAME,CURRENT_VALUE,READONLY,TYPE,PHYSICAL_PRESENCE_REQUIRED,MIN,MAX,");
      foreach ($c in $cs) {
        if ($c.CimClass.CimClassName -ne "HPBIOS_BIOSPassword") {
          if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
            convertSettingToCSV ($c)
          }
        }
      }
      return
    }
    { $_ -eq 'brief' } {
      foreach ($c in $cs) {
        if ((-not $noreadonly.IsPresent) -or ($c.IsReadOnly -eq 0)) {
          Write-Output $c.Name
        }
      }
      return
    }
    default {
      if (-not $noreadonly.IsPresent) {
        return $cs
      }
      else {
        return $cs | Where-Object IsReadOnly -EQ 0
      }
    }
  }
}


<#
.SYNOPSIS
  This is a private function for internal use only
 
.DESCRIPTION
  This is a private function for internal use only
 
.EXAMPLE
 
.NOTES
  - This is a private function for internal use only
#>

function Set-HPPrivateBIOSSettingValuePayload {
  param(
    [Parameter(ParameterSetName = 'Payload',Position = 0,Mandatory = $true,ValueFromPipeline = $true)]
    [string]$Payload
  )

  $portable = $Payload | ConvertFrom-Json

  if ($portable.purpose -ne "hp:sureadmin:biossetting") {
    throw "The payload should be generated by New-HPSureAdminBIOSSettingValuePayload function"
  }

  [SureAdminSetting]$setting = [System.Text.Encoding]::UTF8.GetString($portable.Data) | ConvertFrom-Json

  Set-HPPrivateBIOSSetting -Setting $setting
}

<#
.SYNOPSIS
  Sets the value of a BIOS setting on the current device unless specified for another platform
 
.DESCRIPTION
  This command sets the value of a BIOS setting on the current device unless specified for another platform. Note that some BIOS settings may have various constraints restricting the input that can be provided.
 
.PARAMETER Name
  Specifies the name of a BIOS setting to set. Note that the setting name is usually case sensitive.
 
.PARAMETER Value
  Specifies the new value for the BIOS setting specified in the -Name parameter
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER Password
  Specifies the setup password, if any
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.PARAMETER SkipPrecheck
  If specified, this command skips reading the setting value from the BIOS before applying the new value. This parameter is used for optimization purposes when the setting is guaranteed to exist on the system, or when preparing an HP Sure Admin platform for a remote platform which may contain settings not present on the local platform.
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - By convention, when representing multiple values in an enumeration as a single string, the value with an asterisk in front is the currently active value. For example, given the string "One,*Two,Three" representing three possible enumeration choices, the current active value is "Two".
 
.EXAMPLE
  Set-HPBIOSSettingValue -Name "Asset Tracking Number" -Value "Hello World" -password 's3cr3t'
#>

function Set-HPBIOSSettingValue {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSettingValue")]
  param(
    [Parameter(ParameterSetName = "NewSession",Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 0,Mandatory = $false)]
    [AllowEmptyString()]
    [string]$Password,

    [Parameter(ParameterSetName = "NewSession",Position = 1,Mandatory = $true)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 1,Mandatory = $true)]
    [string]$Name,

    [Parameter(ParameterSetName = "NewSession",Position = 2,Mandatory = $true)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 2,Mandatory = $true)]
    [AllowEmptyString()]
    [string]$Value,

    [Parameter(ParameterSetName = "NewSession",Position = 3,Mandatory = $false)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 3,Mandatory = $false)]
    [switch]$SkipPrecheck,

    [Parameter(ParameterSetName = 'NewSession',Position = 4,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",

    [Parameter(ParameterSetName = 'ReuseSession',Position = 4,Mandatory = $true)]
    [CimSession]$CimSession
  )

  [SureAdminSetting]$setting = New-Object -TypeName SureAdminSetting
  $setting.Name = $Name
  $setting.Value = $Value

  $params = @{
    Setting = $setting
    Password = $Password
    CimSession = $CimSession
    ComputerName = $ComputerName
    SkipPrecheck = $SkipPrecheck
  }
  Set-HPPrivateBIOSSetting @params
}

<#
.SYNOPSIS
  Checks if the BIOS Setup password is set on the current device unless specified for another platform
 
.DESCRIPTION
  This command returns $true if a BIOS password is currently active, or $false otherwise.
 
.PARAMETER ComputerName
 Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.NOTES
  Requires HP BIOS.
 
.EXAMPLE
  Get-HPBIOSSetupPasswordIsSet
 
.LINK
  [Set-HPBIOSSetupPassword](https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSetupPassword)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetupPasswordIsSet)
#>

function Get-HPBIOSSetupPasswordIsSet () {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetupPasswordIsSet")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession

  )
  $params = @{ Name = "Setup Password" }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params
  return [boolean]$obj.IsSet
}

<#
.SYNOPSIS
  Sets the BIOS Setup password on the current device unless specified for another platform
 
.DESCRIPTION
  This command sets the BIOS Setup password to a new password. The password must comply with the current active security policy.
 
.PARAMETER NewPassword
  Specifies the new password. To clear the password, use the Clear-HPBIOSSetupPassword command instead.
 
.PARAMETER Password
  Specifies the existing setup password, if any. If there is a password set, this parameter is required. If there is no password set, providing a value to this parameter has no effect on the outcome. Use the Get-HPBIOSSetupPasswordIsSet command to determine if a password is currently set.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Set-HPBIOSSetupPassword -NewPassword 'newpassword' -Password 'oldpassword'
 
.LINK
  [Clear-HPBIOSSetupPassword](https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSSetupPassword)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetupPasswordIsSet)
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - Multiple attempts to change the password with an incorrect existing password may trigger BIOS lockout mode, which can be cleared by rebooting the system.
#>

function Set-HPBIOSSetupPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/set-HPBIOSSetupPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $true)]
    [string]$NewPassword,

    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 1,Mandatory = $false)]
    [string]$Password,


    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 2,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",

    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 3,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{}
  $settingName = 'Setup Password'

  # if password is set but no Password parameter is provided, throw an error
  if ((Get-HPBIOSSetupPasswordIsSet) -and (-not $Password)) {
    throw [System.ArgumentException]'There is a BIOS Setup password currently set. Please provide it via the -Password parameter to set a new password.'
  }

  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params

  $r = $iface | Invoke-CimMethod -ErrorAction Stop -MethodName 'SetBIOSSetting' -Arguments @{
    Name = $settingName
    Password = '<utf-16/>' + $Password
    Value = '<utf-16/>' + $newPassword
  }

  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw [System.InvalidOperationException]$Err
  }
}

<#
.SYNOPSIS
  Clears the BIOS Setup password on the current device unless specified for another platform
 
.DESCRIPTION
  This command clears the BIOS setup password on the current device unless specified for another platform. To set the password, use the Set-HPBIOSSetupPassword command.
 
.PARAMETER Password
  Specifies the existing setup password. Use the Get-HPBIOSSetupPasswordIsSet command to determine if a password is currently set.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Clear-HPBIOSSetupPassword -Password 'currentpassword'
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - Multiple attempts to change the password with an incorrect existing password may trigger BIOS lockout mode. BIOS lockout mode can be cleared by rebooting the system.
 
.LINK
  [Set-HPBIOSSetupPassword](https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSetupPassword)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetupPasswordIsSet)
#>

function Clear-HPBIOSSetupPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSSetupPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $true)]
    [string]$Password,

    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $settingName = 'Setup Password'


  $params = @{}
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetBiosSetting -Arguments @{ Name = "Setup Password"; Value = "<utf-16/>"; Password = "<utf-16/>" + $Password; }
  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw [System.InvalidOperationException]$Err
  }
}


<#
.SYNOPSIS
  Checks if the BIOS Power-On password is set on the current device unless specified for another platform
 
.DESCRIPTION
  This command returns $true if a BIOS power-on password is currently active, or $false otherwise.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.NOTES
  Changes in the state of the BIOS Power-On Password may not be visible until the system is rebooted and the POST prompt regarding the BIOS Power-On password is accepted.
 
.EXAMPLE
  Get-HPBIOSPowerOnPasswordIsSet
 
.LINK
  [Set-HPBIOSPowerOnPassword](https://developers.hp.com/hp-client-management/doc/Set-HPBIOSPowerOnPassword)
 
.LINK
  [Clear-HPBIOSPowerOnPassword](https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSPowerOnPassword)
#>

function Get-HPBIOSPowerOnPasswordIsSet () {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSPowerOnPasswordIsSet")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession

  )
  $params = @{ Name = "Power-On Password" }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $obj = Get-HPBIOSSetting @params
  return [boolean]$obj.IsSet
}

<#
.SYNOPSIS
  Sets the BIOS Power-On password on the current device unless specified for another platform
 
.DESCRIPTION
  This command sets the BIOS Power-On password on the current device unless specified for another platform. The password must comply with password complexity requirements active on the system.
 
.PARAMETER NewPassword
  Specifies a new password for the BIOS Power-On password. To clear the password, use the Clear-HPBIOSPowerOnPassword command instead.
 
.PARAMETER Password
  Specifies the existing BIOS Setup password (not Power-On password), if any. If there is no BIOS Setup password set, this parameter may be omitted. Use the Get-HPBIOSSetupPasswordIsSet command to determine if a setup password is currently set.
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
.EXAMPLE
  Set-HPBIOSPowerOnPassword -NewPassword 'newpassword' -Password 'setuppassword'
 
.LINK
  [Clear-HPBIOSPowerOnPassword](https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSPowerOnPassword)
 
.LINK
  [Get-HPBIOSPowerOnPasswordIsSet](https://developers.hp.com/hp-client-management/doc/Get-HPBIOSPowerOnPasswordIsSet)
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - On many platform families, changing the Power-On password requires that a BIOS password is active.
  - Changes in the state of the BIOS Power-On Password may not be visible until the system is rebooted and the POST prompt regarding the BIOS Power-On password is accepted.
 
#>

function Set-HPBIOSPowerOnPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSPowerOnPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $true)]
    [string]$NewPassword,
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 1,Mandatory = $false)]
    [string]$Password,

    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 3,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 4,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $settingName = 'Power-On Password'

  $params = @{}
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetBiosSetting -Arguments @{ Name = $settingName; Value = "<utf-16/>" + $newPassword; Password = "<utf-16/>" + $Password; }
  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw $Err
  }
}

<#
.SYNOPSIS
  Clears the BIOS Power-On password on the current device unless specified for another platform
 
.DESCRIPTION
  This command clears any active power-on password on the current device unless specified for another platform.
 
.PARAMETER Password
  Specifies the existing setup (not power-on) password. Use the Get-HPBIOSSetupPasswordIsSet command to determine if a password is currently set. See important note regarding the BIOS Setup Password prerequisite below.
 
.PARAMETER ComputerName
  Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Clear-HPBIOSPowerOnPassword -Password 's3cr3tpassword'
 
.LINK
  [Set-HPBIOSPowerOnPassword](https://developers.hp.com/hp-client-management/doc/Set-HPBIOSPowerOnPassword)
 
.LINK
  [Get-HPBIOSPowerOnPasswordIsSet](https://developers.hp.com/hp-client-management/doc/Get-HPBIOSPowerOnPasswordIsSet)
 
.LINK
  [Get-HPBIOSSetupPasswordIsSet](https://developers.hp.com/hp-client-management/doc/Get-HPBIOSSetupPasswordIsSet)
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
  - For many platform families, an active BIOS password is required to change the Power-On password. If BIOS Setup password is not set, set the BIOS Setup password before using this command.
 
#>

function Clear-HPBIOSPowerOnPassword {
  [CmdletBinding(DefaultParameterSetName = 'NoPassthruNewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Clear-HPBIOSPowerOnPassword")]
  param(
    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 0,Mandatory = $false)]
    [string]$Password,


    [Parameter(ParameterSetName = 'NoPassthruNewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",
    [Parameter(ParameterSetName = 'NoPassthruReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $settingName = 'Power-On Password'


  $params = @{}
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruNewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'NoPassthruReuseSession') { $params.CimSession = $CimSession }

  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetBiosSetting -Arguments @{
    Name = "Power-On Password"
    Value = "<utf-16/>"
    Password = ("<utf-16/>" + $Password)
  }
  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw [System.InvalidOperationException]$Err
  }
}

<#
.SYNOPSIS
  Sets one or more BIOS settings from a file on the current device unless specified for another platform
 
.DESCRIPTION
  This command sets multiple BIOS settings from a file on the current device unless specified for another platform. The file format may be specified via the -Format parameter; however, this command will try to infer the format from the file extension.
 
.PARAMETER File
  Specifies the file to process. This parameter can take in both a relative path and an absolute path. Note that BIOS passwords are not encrypted in this file. Protect the file contents until applied to the target system.
 
.PARAMETER Format
  Specifies the format of the input file in the File parameter. The value must be one of the following values:
    - BCU
    - CSV
    - XML
    - JSON
 
  If not specified, this command will attempt to deduce the format from the file extension and parse accordingly.
 
.PARAMETER Password
  Specifies the current BIOS setup password, if any.
 
.PARAMETER NoSummary
  If specified, this command suppresses the one line summary at the end of the import.
 
.PARAMETER ErrorHandling
  Specifies the type of error handling this command will use. The value must be one of the following values:
    0 - operate normally
    1 - raise exceptions as warnings
    2 - no warnings or exceptions, fail silently
 
.PARAMETER ComputerName
Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Set-HPBIOSSettingValuesFromFile -File .\file.bcu -NoSummary
 
.NOTES
  - Requires HP BIOS.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
#>

function Set-HPBIOSSettingValuesFromFile {
  [CmdletBinding(DefaultParameterSetName = "NotPassThruNewSession",
    HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSettingValuesFromFile")]
  param(
    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 0,Mandatory = $true)]
    [System.IO.FileInfo]$File,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 1,Mandatory = $false)]
    [ValidateSet('XML','JSON','BCU','CSV')]
    [string]$Format = $null,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 2,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 2,Mandatory = $false)]
    [string]$Password,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 3,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 3,Mandatory = $false)]
    [switch]$NoSummary,

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 4,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",

    [Parameter(ParameterSetName = "NotPassThruNewSession",Position = 5,Mandatory = $false)]
    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 5,Mandatory = $false)]
    $ErrorHandling = 2,

    [Parameter(ParameterSetName = "NotPassThruReuseSession",Position = 6,Mandatory = $true)]
    [CimSession]$CimSession
  )

  if (-not $Format) {
    $Format = (Split-Path -Path $File -Leaf).Split(".")[1].ToLower()
    Write-Verbose "Format from file extension: $Format"
  }

  Write-Verbose "Format specified: '$Format'. Reading file..."
  [System.Collections.Generic.List[SureAdminSetting]]$settingsList = Get-HPPrivateSettingsFromFile -FileName $File -Format $Format

  $params = @{
    SettingsList = $settingsList
    ErrorHandling = $ErrorHandling
    ComputerName = $ComputerName
    CimSession = $CimSession
    Password = $Password
    NoSummary = $NoSummary
  }
  Set-HPPrivateBIOSSettingsList @params -Verbose:$VerbosePreference
}

<#
.SYNOPSIS
  Resets the BIOS settings to shipping defaults on the current device unless specified for another platform
 
.DESCRIPTION
  This command resets the BIOS settings to shipping defaults on the current device unless specified for another platform. Please note that the default values are platform-specific.
 
.PARAMETER Password
  Specifies the current BIOS setup password, if any.
 
.PARAMETER ComputerName
  Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
 Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  Set-HPBIOSSettingDefaults -Password 's3cr3t'
 
.NOTES
  - HP BIOS is required.
  - Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
 
#>

function Set-HPBIOSSettingDefaults {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Set-HPBIOSSettingDefaults")]
  param(
    [Parameter(ParameterSetName = "NewSession",Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = "ReuseSession",Position = 0,Mandatory = $false)]
    [AllowEmptyString()]
    [string]$Password,

    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Alias('Target')]
    $ComputerName = ".",

    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $true)]
    [CimSession]$CimSession
  )

  $authorization = "<utf-16/>" + $Password
  Set-HPPrivateBIOSSettingDefaultsAuthorization -ComputerName $ComputerName -CimSession $CimSession -Authorization $authorization -Verbose:$VerbosePreference
}

function Set-HPPrivateBIOSSettingDefaultsAuthorization {
  param(
    [string]$Authorization,
    [string]$ComputerName,
    [CimSession]$CimSession
  )

  Write-Verbose "Calling SetSystemDefaults() on $ComputerName"
  $params = @{}
  if ($CimSession) {
    $params.CimSession = $CimSession
  }
  else {
    $params.CimSession = newCimSession -Target $ComputerName
  }
  $iface = getBiosSettingInterface @params
  $r = $iface | Invoke-CimMethod -MethodName SetSystemDefaults -Arguments @{ Password = $Authorization; }

  if ($r.Return -ne 0) {
    $Err = "$(biosErrorCodesToString($r.Return))"
    throw $Err
  }
}


<#
.SYNOPSIS
  Sets the BIOS Settings defaults payload
 
.DESCRIPTION
  This is a private command for internal use only.
 
.EXAMPLE
 
.NOTES
  - This is a private command for internal use only
#>

function Set-HPPrivateBIOSSettingDefaultsPayload {
  param(
    [Parameter(ParameterSetName = 'Payload',Position = 0,Mandatory = $true,ValueFromPipeline = $true)]
    [string]$Payload
  )

  $portable = $Payload | ConvertFrom-Json

  if ($portable.purpose -ne "hp:sureadmin:resetsettings") {
    throw "The payload should be generated by New-HPSureAdminSettingDefaultsPayload function"
  }

  [SureAdminSetting]$setting = [System.Text.Encoding]::UTF8.GetString($portable.Data) | ConvertFrom-Json

  Set-HPPrivateBIOSSettingDefaultsAuthorization -Authorization $setting.AuthString
}

<#
.SYNOPSIS
  Retrieves the system boot time and uptime of the current device unless specified for another platform
 
.DESCRIPTION
  This command retrieves the system boot time and uptime of the current device unless specified for another platform.
 
.PARAMETER ComputerName
  Specifies a target computer for this command to create its own one-time-use CIMSession object using with. If not specified, this command will use the current device as the target computer for this command. The alias 'Target' can also be used for this parameter.
 
.PARAMETER CimSession
  Specifies a pre-established CIMSession object (as created by the [New-CIMSession](https://docs.microsoft.com/en-us/powershell/module/cimcmdlets/new-cimsessionoption?view=powershell-5.1) command) or a ComputerName in string format for this command to create a one-time-use CIMSession object with
 
 
.EXAMPLE
  (Get-HPDeviceUptime).BootTime
 
#>

function Get-HPDeviceUptime {
  [CmdletBinding(DefaultParameterSetName = 'NewSession',HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceUptime")]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $params = @{
    ClassName = 'Win32_OperatingSystem'
    Namespace = 'root\cimv2'
  }
  if ($PSCmdlet.ParameterSetName -eq 'NewSession') { $params.CimSession = newCimSession -Target $ComputerName }
  if ($PSCmdlet.ParameterSetName -eq 'ReuseSession') { $params.CimSession = $CimSession }

  $result = Get-CimInstance @params -ErrorAction stop
  $resultobject = @{}
  $resultobject.BootTime = $result.LastBootUpTime

  $span = (Get-Date) - ($resultobject.BootTime)
  $resultobject.Uptime = "$($span.days) days, $($span.hours) hours, $($span.minutes) minutes, $($span.seconds) seconds"
  $resultobject
}



<#
.SYNOPSIS
  Retrieves the current boot mode and uptime on the current device
 
.DESCRIPTION
  This command returns an object containing the system uptime, last boot time, whether secure boot is enabled, and whether the system was booted in UEFI or Legacy mode.
 
 
.EXAMPLE
    $IsUefi = (Get-HPDeviceBootInformation).Mode -eq "UEFI"
 
#>

function Get-HPDeviceBootInformation {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceBootInformation")]
  param()

  $mode = @{}

  try {
    $sb = Confirm-SecureBootUEFI
    $mode.Mode = "UEFI"
    $mode.SecureBoot = $sb
  }
  catch {
    $mode.Mode = "Legacy"
    $mode.SecureBoot = $false
  }

  try {
    $uptime = Get-HPDeviceUptime
    $mode.Uptime = $uptime.Uptime
    $mode.BootTime = $uptime.BootTime
  }
  catch {
    $mode.Uptime = "N/A"
    $mode.BootTime = "N/A"
  }

  $mode
}



<#
.SYNOPSIS
  Retrieves available BIOS updates (or downgrades)
 
.DESCRIPTION
  This command uses an internet service to retrieve the list of BIOS updates available for a platform, and optionally checks it against the current system.
 
  The result is a series of records, with the following definition:
 
    - Ver: the BIOS update version
    - Date: the BIOS release date
    - Bin: the BIOS update binary file
 
  Online Mode uses Seamless Firmware Update Service that can update the BIOS in the background while the operating system is running (no authentication needed). 2022 and newer HP computers with Intel processors support Seamless Firmware Update Service.
  Offline Mode then finishes updating the BIOS after reboot and requires authentication (password or payload).
 
.PARAMETER Platform
  Specifies the Platform ID to check. This parameter can be obtained via the Get-HPDeviceProductID command. The Platform ID cannot be specified for a flash operation. If not specified, the current Platform ID is used.
 
.PARAMETER Target
  Specifies the target computer to execute this command. If not specified, the command is executed on the local computer.
 
.PARAMETER Format
  Specifies the format of the result. The value must be one of the following values:
 
    - list: format as a list
    - CSV: format as a comma-separated values list
    - XML: format as XML
    - JSON: format as JSON
 
  If not specified, the default PowerShell formatting is used.
 
.PARAMETER Latest
  If specified, this command returns or downloads the latest available BIOS version between remote and local. If the -Platform parameter is specified, the BIOS on the current device will not be read and the latest BIOS version available remotely will be returned.
 
.PARAMETER Check
  If specified, this command returns true if the latest version corresponds to the installed version or installed version is higher and returns false otherwise. Please note that this parameter is only valid for use when comparing against current platform.
 
.PARAMETER All
  If specified, this command includes all known BIOS update information. This may include additional data such as dependencies, rollback support, and criticality.
 
.PARAMETER Download
  If specified, this command will download the BIOS file to the current directory or a path specified by the -SaveAs parameter.
 
.PARAMETER Flash
  If specified, the BIOS update will be flashed onto the current system.
 
.PARAMETER Password
  Specifies the BIOS password, if any. This parameter is only necessary when the -Flash parameter is specified. Use single quotes around the password to prevent PowerShell from interpreting special characters in the string.
 
.PARAMETER Version
  Specifies the BIOS version to download and/or flash. If not specified, the latest version will be used. This parameter must be specified with the -Download parameter and/or -Flash parameter.
 
.PARAMETER SaveAs
  Specifies the file name for the downloaded BIOS file. If not specified, the remote file name will be used.
 
.PARAMETER Quiet
  If specified, this command will not display a progress bar during the BIOS file download.
 
.PARAMETER Overwrite
  If specified, this command will force overwrite any existing file with the same name during BIOS file download. This command is only necessary when the -Download parameter is used.
 
.PARAMETER Yes
  If specified, this command will show an 'Are you sure you want to flash' prompt. This parameter prevents users from accidentally flashing the BIOS.
 
.PARAMETER Force
  If specified, this command forces the BIOS update even if the target BIOS is already installed.
 
.PARAMETER BitLocker
  Specifies the behavior to the BitLocker check prompt (if any). The value must be one of the following values:
  - stop: (default option) stops execution if BitLocker is detected but not suspended, and prompts
  - ignore: skips the BitLocker check
  - suspend: suspend sBitLocker if active and continues with execution
 
.PARAMETER Url
  Specifies an alternate Url source for the platform's BIOS update catalog (xml). This URL must be HTTPS.
 
.PARAMETER Offline
  If specified, this command uses the offline mode to flash the BIOS instead of the default online mode. In offline mode, the actual flash will occur after reboot at pre-OS environment. Please note that offline mode is selected by default when downgrading the BIOS version and requires authentication so either a Password or a PayloadFile should be specified.
 
.PARAMETER NoWait
  If specified, the script does not wait for the online flash background task to finish. If the user reboots the PC during the online flash, the BIOS update will complete only after reboot.
 
.NOTES
  - Flash is only supported on Windows 10 1709 (Fall Creators Updated) and later.
  - UEFI boot mode is required for flashing; legacy mode is not supported.
  - The flash operation requires 64-bit PowerShell (not supported under 32-bit PowerShell).
 
  **WinPE notes**
 
  - Use '-BitLocker ignore' when using this command in WinPE because BitLocker checks are not applicable in Windows PE.
  - Requires that the WinPE image is built with the WinPE-SecureBootCmdlets.cab component.
 
.EXAMPLE
  Get-HPBIOSUpdates
 
.EXAMPLE
  Get-HPBIOSUpdates -Platform "87ED"
 
.EXAMPLE
  Get-HPBIOSUpdates -Download -Version "01.26.00"
 
.EXAMPLE
  Get-HPBIOSUpdates -Flash -Version "01.26.00"
#>

function Get-HPBIOSUpdates {

  [CmdletBinding(DefaultParameterSetName = "ViewSet",
    HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSUpdates")]
  param(
    [Parameter(ParameterSetName = "DownloadSet",Position = 0,Mandatory = $false)]
    [Parameter(ParameterSetName = "ViewSet",Position = 0,Mandatory = $false)]
    [Parameter(Position = 0,Mandatory = $false)]
    [ValidatePattern("^[a-fA-F0-9]{4}$")]
    [string]$Platform,

    [ValidateSet('XML','JSON','CSV','List')]
    [Parameter(ParameterSetName = "ViewSet",Position = 1,Mandatory = $false)]
    [string]$Format,

    [Parameter(ParameterSetName = "ViewSet",Position = 2,Mandatory = $false)]
    [switch]$Latest,

    [Parameter(ParameterSetName = "CheckSet",Position = 3,Mandatory = $false)]
    [switch]$Check,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 4,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 4,Mandatory = $false)]
    [Parameter(ParameterSetName = "ViewSet",Position = 4,Mandatory = $false)]
    [string]$Target = ".",

    [Parameter(ParameterSetName = "ViewSet",Position = 5,Mandatory = $false)]
    [switch]$All,

    [Parameter(ParameterSetName = "DownloadSet",Position = 6,Mandatory = $true)]
    [switch]$Download,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 7,Mandatory = $true)]
    [switch]$Flash,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 8,Mandatory = $false)]
    [string]$Password,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 9,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 9,Mandatory = $false)]
    [string]$Version,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 10,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 10,Mandatory = $false)]
    [string]$SaveAs,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 11,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 11,Mandatory = $false)]
    [switch]$Quiet,

    [Parameter(ParameterSetName = "FlashSetPassword",Position = 12,Mandatory = $false)]
    [Parameter(ParameterSetName = "DownloadSet",Position = 12,Mandatory = $false)]
    [switch]$Overwrite,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 13,Mandatory = $false)]
    [switch]$Yes,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 14,Mandatory = $false)]
    [ValidateSet('Stop','Ignore','Suspend')]
    [string]$BitLocker = 'Stop',

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 15,Mandatory = $false)]
    [switch]$Force,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 16,Mandatory = $false)]
    [string]$Url = "https://ftp.hp.com/pub/pcbios",

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 17,Mandatory = $false)]
    [switch]$Offline,

    [Parameter(ParameterSetName = 'FlashSetPassword',Position = 18,Mandatory = $false)]
    [switch]$NoWait
  )

  if($PSCmdlet.ParameterSetName -eq "FlashSetPassword"){
    # check for elevated admin now, check for Sure Admin later
    Test-HPFirmwareFlashSupported -CheckPlatform
  }

  # only allow https or file paths with or without file:// URL prefix
  if ($Url -and -not ($Url.StartsWith("https://",$true,$null) -or [System.IO.Directory]::Exists($Url) -or $Url.StartsWith("file://",$true,$null))) {
    throw [System.ArgumentException]"Only HTTPS or valid existing directory paths are supported."
  }

  if (-not $platform) {
    # if platform is not provided, $platform is current platform
    $platform = Get-HPDeviceProductID -Target $target
  }

  $platform = $platform.ToUpper()
  Write-Verbose "Using platform ID $platform"

  $uri = [string]"$Url/{0}/{0}.xml" -f $platform.ToUpper()
  Write-Verbose "Retrieving catalog file $uri"
  $ua = Get-HPPrivateUserAgent

  # access xml file
  try {
    [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
    $data = Invoke-WebRequest -Uri $uri -UserAgent $ua -UseBasicParsing -ErrorAction Stop
  }
  catch {
    # checking 404 based on exception message
    # bc PS5 throws WebException while PS7 throws httpResponseException
    # bc PS5 is based on WebRequest while PS7 is based on HttpClient
    if ($_.Exception.Message.contains("(404) Not Found") -or $_.Exception.Message.contains("404 (Not Found)")){
      throw [System.Management.Automation.ItemNotFoundException]"Unable to retrieve BIOS data for a platform with ID $platform (data file not found)."
    }

    throw $_.Exception
  }

  # read xml file
  try {
    [xml]$doc = [System.IO.StreamReader]::new($data.RawContentStream).ReadToEnd()
  }
  catch {
    throw [System.FormatException]"Unable to read data: $($_.Exception.Message)"
  }

  # in the case that the xml file is empty, or the xml file is not in the expected format i.e. no Rel entries, we will catch it here
  if ((-not $doc) -or (-not (Get-Member -InputObject $doc -Type Property -Name "BIOS")) -or (-not (Get-Member -InputObject $doc.bios -Type Property -Name "Rel")))
  {
    throw [System.FormatException]"There are currently no BIOS updates available for your platform."
  }

  # reach to Rel nodes to find Bin entries in xml
  # ignore any entry not ending in *.bin e.g. *.tgz, *.cab
  $unwanted_nodes = $doc.SelectNodes("//BIOS/Rel") | Where-Object { -not ($_.Bin -like "*.bin") }
  $unwanted_nodes | Where-Object {
    $ignore = $_.ParentNode.RemoveChild($_)
  }

  # trim the 0 from the start of the version and then sort on the version value
  $refined_doc = $doc.SelectNodes("//BIOS/Rel") | Select-Object -Property @{ Name = 'Ver'; expr = { $_.Ver.TrimStart("0") } },'Date','Bin','RB','L','DP' `
     | Sort-Object -Property Ver -Descending

  # latest version
  $latestVer = $refined_doc[0]

  if (($PSCmdlet.ParameterSetName -eq "ViewSet") -or ($PSCmdlet.ParameterSetName -eq "CheckSet")) {
    Write-Verbose "Proceeding with parameter set => view"
    if ($check.IsPresent -eq $true) {
      [string]$haveVer = Get-HPBIOSVersion -Target $target
      # check should return true if local BIOS is same or newer than the latest available remote BIOS.
      return ([string]$haveVer.TrimStart("0") -ge [string]$latestVer[0].Ver)
    }

    $args = @{}
    if ($all.IsPresent) {
      $args.Property = (@{ Name = 'Ver'; expr = { $_.Ver.TrimStart("0") } },"Date","Bin",`
           (@{ Name = 'RollbackAllowed'; expr = { [bool][int]$_.RB.trim() } }),`
           (@{ Name = 'Importance'; expr = { [Enum]::ToObject([BiosUpdateCriticality],[int]$_.L.trim()) } }),`
           (@{ Name = 'Dependency'; expr = { [string]$_.DP.trim() } }))
    }
    else {
      $args.Property = (@{ Name = 'Ver'; expr = { $_.Ver.TrimStart("0") } },"Date","Bin")
    }

    # for current platform: latest should return whichever is latest, between local and remote.
    # for any other platform specified: latest should return latest entry from SystemID.XML since we don't know local BIOSVersion
    if ($latest)
    {
      if ($PSBoundParameters.ContainsKey('Platform'))
      {
        # platform specified, do not read information from local system and return latest platform published
        $args.First = 1
      }
      else {
        $retrieved = 0
        # determine the local BIOS version
        [string]$haveVer = Get-HPBIOSVersion -Target $target
        # latest should return whichever is latest, between local and remote for current system.
        if ([string]$haveVer -ge [string]$latestVer[0].Ver)
        {
          # local is the latest. So, retrieve attributes other than BIOSVersion to print for latest
          for ($i = 0; $i -lt $refined_doc.Length; $i++) {
            if ($refined_doc[$i].Ver -eq $haveVer) {
              $haveVerFromDoc = $refined_doc[$i]
              $pso = [pscustomobject]@{
                Ver = $haveVerFromDoc.Ver
                Date = $haveVerFromDoc.Date
                Bin = $haveVerFromDoc.Bin
              }
              if ($all) {
                $pso | Add-Member -MemberType ScriptProperty -Name RollbackAllowed -Value { [bool][int]$haveVerFromDoc.RB.trim() }
                $pso | Add-Member -MemberType ScriptProperty -Name Importance -Value { [Enum]::ToObject([BiosUpdateCriticality],[int]$haveVerFromDoc.L.trim()) }
                $pso | Add-Member -MemberType ScriptProperty -Name Dependency -Value { [string]$haveVerFromDoc.DP.trim }
              }
              $retrieved = 1
              if ($pso) {
                formatBiosVersionsOutputList ($pso)
                return
              }
            }
          }
          if ($retrieved -eq 0) {
            Write-Verbose "Retrieving entry from XML failed, get the information from CIM class."
            # calculating date from Win32_BIOS
            $year = (Get-CimInstance Win32_BIOS).ReleaseDate.Year
            $month = (Get-CimInstance Win32_BIOS).ReleaseDate.Month
            $day = (Get-CimInstance Win32_BIOS).ReleaseDate.Day
            $date = $year.ToString() + '-' + $month.ToString() + '-' + $day.ToString()
            Write-Verbose "Date calculated from CIM Class is: $date"

            $currentVer = Get-HPBIOSVersion
            $pso = [pscustomobject]@{
              Ver = $currentVer
              Date = $date
              Bin = $null
            }
            if ($all) {
              $pso | Add-Member -MemberType ScriptProperty -Name RollbackAllowed -Value { $null }
              $pso | Add-Member -MemberType ScriptProperty -Name Importance -Value { $null }
              $pso | Add-Member -MemberType ScriptProperty -Name Dependency -Value { $null }
            }
            if ($pso) {
              $retrieved = 1
              formatBiosVersionsOutputList ($pso)
              return
            }
          }
        }
        else {
          # remote is the latest
          $args.First = 1
        }
      }
    }
    formatBiosVersionsOutputList ($refined_doc | Sort-Object -Property ver -Descending | Select-Object @args)
  }
  else {
    $download_params = @{}

    if ($version) {
      $version = $version.TrimStart('0')
      $latestVer = $refined_doc `
         | Where-Object { $_.Ver.TrimStart("0") -eq $version } `
         | Select-Object -Property Ver,Bin -First 1
    }

    if (-not $latestVer) { throw [System.ArgumentOutOfRangeException]"Version $version was not found." }

    if (($flash.IsPresent) -and (-not $saveAs)) {
      $saveAs = Get-HPPrivateTemporaryFileName -FileName $latestVer.Bin
      $download_params.NoClobber = "yes"
      Write-Verbose "Temporary file name for download is $saveAs"
    }
    else { $download_params.NoClobber = if ($overwrite.IsPresent) { "yes" } else { "no" } }

    Write-Verbose "Proceeding with parameter set => download, overwrite=$($download_params.NoClobber)"

    $remote_file = $latestVer.Bin
    $local_file = $latestVer.Bin
    $remote_ver = $latestVer.Ver

    if ($PSCmdlet.ParameterSetName -eq "FlashSetPassword") {
      $running = Get-HPBIOSVersion
      $offlineMode = $false

      Test-HPFirmwareFlashSupported -CheckPlatform

      # Check the BIOS Update Credential Policy setting
      $credentialPolicyValue = $null
      try{
        $credentialPolicyValue = Get-HPBIOSSettingValue -Name "BIOS Update Credential Policy" -Verbose:$VerbosePreference
      }
      catch {
        Write-Verbose "Exception caught retrieving BIOS Update Credential Policy. Will continue with normal process: $($_.Exception.Message)"
      }

      # When Sure Admin is enabled, password is no longer accepted
      if ((Get-HPPrivateIsSureAdminEnabled) -eq $true) {
        if($credentialPolicyValue -eq "Always Require Credentials"){
          throw "Sure Admin is enabled, and BIOS Update Credential Policy is set to 'Always Require Credentials'. You must use Update-HPFirmware with a payload instead of a password"
        }
        elseif ($credentialPolicyValue -eq "Require Credentials on Downgrade Only"){
          if ([Version]$running.trim() -gt [Version]$remote_ver.trim()) {
            throw "Downgrade Detected, Sure Admin is enabled, and BIOS Update Credential Policy is set to 'Require Credentials on Downgrade Only'. You must use Update-HPFirmware with a payload instead of a password"
          }     
        }
        elseif($null -eq $credentialPolicyValue){
          # BIOS Update Credential Policy setting does not exist, throw original error
          throw "Sure Admin is enabled, you must use Update-HPFirmware with a payload instead of a password"
        }
        # elseif ($credentialPolicyValue -eq "Never Require Credentials"){ # no error, continue with the process
      }
      elseif((Get-HPBIOSSetupPasswordIsSet) -and -not $Password){
        if($credentialPolicyValue -eq "Always Require Credentials"){
          throw "Setup Password is set, and BIOS Update Credential Policy is set to 'Always Require Credentials'. Please provide a password to continue with the update."
        }
        elseif ($credentialPolicyValue -eq "Require Credentials on Downgrade Only"){
          if ([Version]$running.trim() -gt [Version]$remote_ver.trim()) {
            throw "Downgrade Detected, Setup Password is set, and BIOS Update Credential Policy is set to 'Require Credentials on Downgrade Only'. Please provide a password to continue with the downgrade."
          }     
        }
      }

      if ([Version]$running.trim() -ge [Version]$remote_ver.trim()) {
        if ($Force.IsPresent) {
          $offlineMode = $true
          Write-Verbose "Offline mode selected to downgrade BIOS"
        }
        else {
          Write-Host "This system is already running BIOS version $($remote_ver.TrimStart(`"0`").Trim()) or newer."
          Write-Host -ForegroundColor Cyan "You can specify -Force on the command line to proceed anyway."
          return
        }
      }
      if (-not $offlineMode -and $Offline.IsPresent) {
        $offlineMode = $true
        Write-Verbose "Offline mode selected"
      }
    }

    if ($saveAs) {
      $local_file = $saveAs
    }

    [Environment]::CurrentDirectory = $pwd
    #if (-not [System.IO.Path]::IsPathRooted($to)) { $to = ".\$to" }

    $download_params.url = [string]"$Url/{0}/{1}" -f $platform,$remote_file
    $download_params.Target = [IO.Path]::GetFullPath($local_file)
    $download_params.progress = ($quiet.IsPresent -eq $false)
    Invoke-HPPrivateDownloadFile @download_params -panic

    if ($PSCmdlet.ParameterSetName -eq "FlashSetPassword") {
      if (-not $yes) {
        Write-Host -ForegroundColor Cyan "Are you sure you want to flash this system with version '$remote_ver'?"
        Write-Host -ForegroundColor Cyan "Current BIOS version is $(Get-HPBIOSVersion)."
        Write-Host -ForegroundColor Cyan "A reboot will be required for the operation to complete."
        $response = Read-Host -Prompt "Type 'Y' to continue and anything else to abort. Or specify -Yes on the command line to skip this prompt"
        if ($response -ne "Y") {
          Write-Verbose "User did not confirm and did not disable confirmation - aborting."
          return
        }
      }

      Write-Verbose "Passing to flash process with file $($download_params.target)"

      $update_params = @{
        file = $download_params.Target
        bitlocker = $bitlocker
        Force = $Force
        Password = $password
      }

      Update-HPFirmware @update_params -Verbose:$VerbosePreference -Offline:$offlineMode -NoWait:$NoWait
    }
  }

}

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

  $params = @{
    ClassName = 'Win32_BIOS'
    Namespace = 'root\cimv2'
  }
  $params.CimSession = newCimSession -Target "."
  $obj = Get-CimInstance @params -ErrorAction stop
  $verfield = (getWmiField $obj "SMBIOSBIOSVersion").Split()

  return $verfield[0],$verfield[2]
}


<#
.SYNOPSIS
  Retrieves the available BIOS updates using Windows Update packages
 
.DESCRIPTION
  This command retrieves the available BIOS updates using Windows Update package by using an internet service to retrieve the list of BIOS capsule updates available for a platform family, and optionally install the update in the current system. The versions available through this command may differ from the Get-HPBIOSUpdate command since this command relies on the Microsoft capsules availability. The availability of the updates can be delayed due to the Windows Update in-flight processes.
 
.PARAMETER Family
  Specifies the platform family to retrieve. If not specified, this command retrieves and applies the current platform family.
 
.PARAMETER Severity
  If specified, this command returns the available BIOS for the specified severity: 'Latest' or 'LatestCritical'.
 
.PARAMETER Download
  If specified, this command downloads the BIOS file to the current directory or a path specified by the -SaveAs parameter.
 
.PARAMETER Flash
  If specified, this command checks and applies the BIOS update to the current system.
 
.PARAMETER Version
  Specifies the BIOS version to download. If not specified, the latest version available will be downloaded.
 
.PARAMETER SaveAs
  Specifies the file name for the downloaded BIOS file. If not specified, the remote file name will be used.
  In order to use the downloaded file with the Add-HPBIOSWindowsUpdateScripts command, the file name must follow the standard: platform family (3 digit) + underscore + BIOS version (6 digits) + .cab, for instance: R70_011200.cab
 
.PARAMETER Yes
  If specified, this command will show an 'Are you sure you want to flash' prompt. This parameter prevents users from accidentally flashing the BIOS.
 
.PARAMETER Force
  If specified, this command forces the BIOS to update even if the target BIOS is already installed.
 
.PARAMETER Url
  Specifies an alternate Url source for the platform's BIOS update catalog (xml). This URL must be HTTPS.
 
.PARAMETER Quiet
  If specified, this command will not display a progress bar during the BIOS file download.
 
.PARAMETER List
  If specified, this command will display a list with all the BIOS versions available for the specified platform.
 
.NOTES
  - Requires Windows group policy support
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -List -Family R70
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity Latest
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity LatestCritical
 
.EXAMPLE
  Get-HPBIOSWindowsUpdate -Flash -Severity LatestCritical -Family R70
 
#>

function Get-HPBIOSWindowsUpdate {
  [CmdletBinding(DefaultParameterSetName = "Severity",HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPBIOSWindowsUpdate")]
  param(
    [Parameter(Mandatory = $false,Position = 0,ParameterSetName = "Severity")]
    [ValidateSet('Latest','LatestCritical')]
    [string]$Severity = 'Latest',

    [Parameter(Mandatory = $true,Position = 0,ParameterSetName = "Specific")]
    [string]$Version,

    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "Specific")]
    [Parameter(Mandatory = $false,Position = 0,ParameterSetName = "List")]
    [string]$Family,

    [Parameter(Mandatory = $false,Position = 2,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 2,ParameterSetName = "Specific")]
    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "List")]
    [string]$Url = "https://hpia.hpcloud.hp.com/downloads/capsule",

    [Parameter(Mandatory = $false,Position = 3,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 3,ParameterSetName = "Specific")]
    [switch]$Quiet,

    [Parameter(Mandatory = $false,Position = 4,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 4,ParameterSetName = "Specific")]
    [string]$SaveAs,

    [Parameter(Mandatory = $false,Position = 5,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 5,ParameterSetName = "Specific")]
    [switch]$Download,

    [Parameter(Mandatory = $false,Position = 6,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 6,ParameterSetName = "Specific")]
    [switch]$Flash,

    [Parameter(Mandatory = $false,Position = 7,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 7,ParameterSetName = "Specific")]
    [switch]$Yes,

    [Parameter(Mandatory = $false,Position = 8,ParameterSetName = "Severity")]
    [Parameter(Mandatory = $false,Position = 8,ParameterSetName = "Specific")]
    [switch]$Force,

    [Parameter(Mandatory = $true,Position = 2,ParameterSetName = "List")]
    [switch]$List
  )

   # only allow https or file paths with or without file:// URL prefix
  if ($Url -and -not ($Url.StartsWith("https://",$true,$null) -or [System.IO.Directory]::Exists($Url) -or $Url.StartsWith("file://",$true,$null))) {
  throw [System.ArgumentException]"Only HTTPS or valid existing directory paths are supported."
  }

  if ($Family -and -not $Version) {
    $_,$biosVersion = Get-HPPrivateBIOSFamilyNameAndVersion
    $biosFamily = $Family
  }
  elseif (-not $Family -and $Version) {
    $biosFamily,$_ = Get-HPPrivateBIOSFamilyNameAndVersion
    $biosVersion = $Version
  }
  elseif (-not $Version -and -not $Family) {
    $biosFamily,$biosVersion = Get-HPPrivateBIOSFamilyNameAndVersion
  } else {
    $biosFamily = $Family
    $biosVersion = $Version
  }

  [string]$uri = [string]"$Url/{0}/{0}.json" -f $biosFamily.ToUpper()
  Write-Verbose "Retrieving $biosFamily catalog $uri"
  Write-Verbose "BIOS Version: $biosVersion"

  $ua = Get-HPPrivateUserAgent
  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  try {
    $data = Invoke-WebRequest -Uri $uri -UserAgent $ua -UseBasicParsing -ErrorAction Stop
  }
  catch {
    Write-Verbose $_.Exception
    throw [System.Management.Automation.ItemNotFoundException]"Platform family $biosFamily is not currently supported. Unable to retrieve the $biosFamily BIOS update catalog. For list of supported platforms, please visit https://ftp.ext.hp.com/pub/caps-softpaq/cmit/imagepal/ref/platformList.html"
  }

  $doc = [System.IO.StreamReader]::new($data.RawContentStream).ReadToEnd() | ConvertFrom-Json

  if ($List.IsPresent) {
    $data = $doc | Sort-Object -Property biosVersion -Descending
    return $data | Format-Table -Property biosFamily,biosVersion,severity,isLatest,IsLatestCritical
  }

  if ($PSCmdlet.ParameterSetName -eq "Specific") {
    $filter = $doc | Where-Object { $_.BiosVersion -eq $biosVersion } # specific
    Write-Verbose "Locating a specific version"
    if ($null -eq $filter) {
      throw "The version specified is not available on the $biosFamily catalog"
    }
  }
  elseif ($Severity -eq "LatestCritical") {
    $filter = $doc | Where-Object { $_.isLatestCritical -eq $true } # latest critical
    Write-Verbose "Locating the latest critical version available"
  }
  else {
    $filter = $doc | Where-Object { $_.isLatest -eq $true } # latest
    Write-Verbose "Locating the latest version available"
  }

  $sort = $filter | Sort-Object -Property biosVersion -Descending
  @{
    Family = $sort[0].biosFamily
    Version = $sort[0].BiosVersion
  }

  if ($Flash.IsPresent) {
    $running = Get-HPBIOSVersion

    # downgrade is not supported for Windows Updates, so regardless of value of BIOS Update Credential Policy,
    # we will throw an error
    if([Version]$running.trim() -gt [Version]$sort[0].BiosVersion.trim()) { # downgrade
      throw "Will not continue with update. Downgrade detected. Downgrade is not supported for Windows Updates."
    }

    $credentialPolicyValue = $null
    try{
      # at this point, it is an upgrade or update to same version
      $credentialPolicyValue = Get-HPBIOSSettingValue -Name "BIOS Update Credential Policy" -Verbose:$VerbosePreference
    }
    catch{
      Write-Verbose "Exception caught retrieving BIOS Update Credential Policy. Will continue with normal process: $($_.Exception.Message)"
    }

    if($credentialPolicyValue -eq "Always Require Credentials") {
      throw "Will not continue with update. BIOS Update Credential Policy is set to 'Always Require Credentials'."
    }
    elseif($credentialPolicyValue -eq "Require Credentials on Downgrade Only"){
      Write-Verbose "Continue to apply update. Upgrade or update to same version found, and BIOS Update Credential Policy is set to 'Require Credentials on Downgrade Only'."   
    }
    else{ # Never Require Credentials or does not exist
      Write-Verbose "Continue to apply update. BIOS Update Credential Policy is set to 'Never Require Credentials' or does not exist."
    }

    if (-not $Yes.IsPresent) {
      Write-Host -ForegroundColor Cyan "Are you sure you want to flash this system with version '$($sort[0].biosVersion)'?"
      Write-Host -ForegroundColor Cyan "Current BIOS version is $running."
      Write-Host -ForegroundColor Cyan "A reboot will be required for the operation to complete."
      $response = Read-Host -Prompt "Type 'Y' to continue and anything else to abort. Or specify -Yes on the command line to skip this prompt"
      if ($response -ne "Y") {
        Write-Verbose "User did not confirm and did not disable confirmation - aborting."
        return
      }
    }
    if ((-not $Force.IsPresent) -and $running.TrimStart("0").trim() -ge $sort[0].BiosVersion.TrimStart("0").trim()) {
      Write-Host "This system is already running BIOS version $($sort[0].biosVersion) or newer."
      Write-Host -ForegroundColor Cyan "You can specify -Force on the command line to proceed anyway."
      return
    }
  }

  if ($Download.IsPresent -or $Flash.IsPresent) {
    Write-Verbose "Download from $($sort[0].url)"
    if ($SaveAs) {
      $localFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs)
    } else {
      $extension = ($sort[0].url -split '\.')[-1]
      $SaveAs = Get-HPPrivateTemporaryFileName -FileName "$($sort[0].biosFamily)_$($sort[0].biosVersion -Replace '\.').$extension"
      $localFile = [IO.Path]::GetFullPath($SaveAs)
    }
    Write-Verbose "LocalFile: $localFile"

    $download_params = @{
      NoClobber = "yes"
      url = $sort[0].url 
      Target = $localFile
      progress = ($Quiet.IsPresent -eq $false)
    }

    try {
      Invoke-HPPrivateDownloadFile @download_params -Verbose:$VerbosePreference
    }
    catch {
      throw [System.Management.Automation.ItemNotFoundException]"Unable to download the BIOS update archive from $($download_params.url): $($_.Exception)"
    }
    Write-Host "Saved as $localFile"

    $hash = (Get-FileHash $localFile -Algorithm SHA1).Hash
    $bytes = [byte[]] -split ($hash -replace '..','0x$& ')
    $base64 = [System.Convert]::ToBase64String($bytes)
    if ($base64 -eq $sort[0].digest) {
      Write-Verbose "Integrity check passed"
    }
    else {
      throw "Cab file integrity check failed"
    }
  }

  if ($Flash.IsPresent) {
    Add-HPBIOSWindowsUpdateScripts -WindowsUpdateFile $localFile
  }
}

function Get-HPPrivatePSScriptsEntries {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $false,Position = 0)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $types = '[Logon]','[Logoff]','[Startup]','[Shutdown]'
  $cmdLinesSet = @{}
  $parametersSet = @{}

  if ([System.IO.File]::Exists($Path)) {
    $contents = Get-Content $Path
    if ($contents) {
      for ($i = 0; $i -lt $contents.Length; $i++) {
        if ($types.contains($contents[$i])) {
          $t = $contents[$i]
          $cmdLinesSet[$t] = [System.Collections.ArrayList]@()
          $parametersSet[$t] = [System.Collections.ArrayList]@()
          continue
        }
        if ($contents[$i].Length -gt 0) {
          $cmdLinesSet[$t].Add($contents[$i].substring(1)) | Out-Null
          $parametersSet[$t].Add($contents[$i + 1].substring(1)) | Out-Null
          $i++
        }
      }
    }
  }

  $cmdLinesSet,$parametersSet
}

function Set-HPPrivatePSScriptsEntries {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true,Position = 0)]
    $CmdLines,

    [Parameter(Mandatory = $true,Position = 1)]
    $Parameters,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $types = '[Logon]','[Logoff]','[Startup]','[Shutdown]'
  $contents = ""
  foreach ($type in $types) {
    if ($CmdLines.contains($type)) {
      for ($i = 0; $i -lt $CmdLines[$type].Count; $i++) {
        if ($i -eq 0) {
          $contents += "$type`n"
        }
        $contents += "$($i)$($CmdLines[$type][$i])`n"
        $contents += "$($i)$($Parameters[$type][$i])`n"
      }
      $contents += "`n"
    }
  }

  if (-not [System.IO.File]::Exists($Path)) {
    New-Item -Force -Path $Path -Type File
  }
  $contents | Set-Content -Path $Path -Force
}

<#
.SYNOPSIS
  Adds a PowerShell script to run at Startup or Shutdown
 
.DESCRIPTION
  This command adds a PowerShell script to the group policy that runs at Startup or Shutdown. This command is invoked by the Add-HPBIOSWindowsUpdateScripts command.
 
.PARAMETER Type
  Specifies the type of script should run at Startup or Shutdown. The value of this parameter must be either 'Startup' or 'Shutdown'.
 
.PARAMETER CmdLine
  Specifies a command line for a PowerShell script
 
.PARAMETER Parameters
  Specifies the parameters to be passed to the script at its execution time
 
.PARAMETER Path
  If specified, a custom path can be used.
 
.EXAMPLE
  Add-PSScriptsEntry -Type 'Shutdown' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Add-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Add-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1' -Parameters 'myparam'
#>

function Add-PSScriptsEntry
{
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Add-PSScriptsEntry")]
  param(
    [ValidateSet('Startup','Shutdown')]
    [Parameter(Mandatory = $true,Position = 0)]
    [string]$Type,

    [Parameter(Mandatory = $true,Position = 1)]
    [string]$CmdLine,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$Parameters,

    [Parameter(Mandatory = $false,Position = 3)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $cmdLinesSet,$parametersSet = Get-HPPrivatePSScriptsEntries -Path $Path

  if (-not $cmdLinesSet.ContainsKey("[$Type]")) {
    $cmdLinesSet["[$Type]"] = [System.Collections.ArrayList]@()
  }
  if (-not $parametersSet.ContainsKey("[$Type]")) {
    $parametersSet["[$Type]"] = [System.Collections.ArrayList]@()
  }

  if (-not $cmdLinesSet["[$Type]"].contains("CmdLine=$CmdLine")) {
    $cmdLinesSet["[$Type]"].Add("CmdLine=$CmdLine") | Out-Null
    $parametersSet["[$Type]"].Add("Parameters=$Parameters") | Out-Null
  }

  Set-HPPrivatePSScriptsEntries -CmdLines $cmdLinesSet -Parameters $parametersSet -Path $Path
}

<#
.SYNOPSIS
  Retrieves the HP-CMSL environment configuration
 
.DESCRIPTION
  This command returns environment information to help debug issues.
 
.EXAMPLE
  Get-HPCMSLEnvironment > MyEnvironment.txt
#>

function Get-HPCMSLEnvironment {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPCMSLEnvironment")]
  param()

  Get-ComputerInfo
  $psVersionTable
  try {
    $psISE
  }
  catch {
    'Not running on Windows PowerShell ISE'
  }

  $modules = @(
    'HP.Consent',
    'HP.Private',
    'HP.Utility',
    'HP.ClientManagement',
    'HP.Firmware',
    'HP.Notifications',
    'HP.Sinks',
    'HP.Retail',
    'HP.Softpaq',
    'HP.Repo',
    'HP.SmartExperiences',
    'HP.Displays',
    'HP.Security'
  )

  $modulesFullVersion = @{}
  foreach ($module in $modules) {
    $m = Get-Module -Name $module
    if ($null -eq $m) {
      $m = Get-Module -Name $module -ListAvailable
    }

    if($null -eq $m) { # if not imported or installed, just continue to next module
      $modulesFullVersion[$module] = $null
      continue
    }

    $path = "$($m.ModuleBase)\$module.psd1"
    $line = Select-String -Path $path -Pattern "FullModuleVersion = '(.+)'"

    if ($null -eq $line -or $line.PSobject.Properties.name -notcontains 'Matches') {
      $modulesFullVersion[$module] = $null
      continue
    }
    $lineMatch = $line.Matches.Value
    $lineMatch -match "'(.+)'" | Out-Null
    $fullModuleVersion = $Matches[1]
    $modulesFullVersion[$module] = $fullModuleVersion
  }
  $modulesFullVersion
  @{
    SystemID = Get-HPDeviceProductID
    Os = Get-HPPrivateCurrentOs
    OsVer = Get-HPPrivateCurrentDisplayOSVer
    Bitness = Get-HPPrivateCurrentOsBitness
  }
}

<#
.SYNOPSIS
  Removes a PowerShell script from the group policy
 
.DESCRIPTION
  This command removes a PowerShell script from the group policy that runs at Startup or Shutdown. This command returns true if any entry was removed. This command is invoked by the Add-HPBIOSWindowsUpdateScripts command.
 
.PARAMETER Type
  Specifies the type of script that should run at Startup or Shutdown. The value of this parameter must be either 'Startup' or 'Shutdown'.
 
.PARAMETER CmdLine
  Specifies a command line for a PowerShell script
 
.PARAMETER Parameters
  Specifies the parameters to be passed to the script at its execution time
 
.PARAMETER Path
  If specified, a custom path can be used.
 
.EXAMPLE
  Remove-PSScriptsEntry -Type 'Shutdown' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Remove-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1'
 
.EXAMPLE
  Remove-PSScriptsEntry -Type 'Startup' -CmdLine 'myscript.ps1' -Parameters 'myparam'
#>

function Remove-PSScriptsEntry {
  [CmdletBinding(HelpUri = "https://developers.hp.com/hp-client-management/doc/Remove-PSScriptsEntry")]
  param(
    [ValidateSet('Startup','Shutdown')]
    [Parameter(Mandatory = $true,Position = 0)]
    [string]$Type,

    [Parameter(Mandatory = $true,Position = 1)]
    [string]$CmdLine,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$Parameters,

    [Parameter(Mandatory = $false,Position = 3)]
    [string]$Path = "${env:SystemRoot}\System32\GroupPolicy\Machine\Scripts\psscripts.ini"
  )

  $cmdLinesSet,$parametersSet = Get-HPPrivatePSScriptsEntries -Path $Path

  if (-not $cmdLinesSet.ContainsKey("[$Type]") -and -not $parametersSet.ContainsKey("[$Type]")) {
    # File doesn't contain the type specified. There is nothing to be removed
    return
  }

  $removed = $false
  # If a parameter is specified we remove only the scripts with the specified parameter from the file
  while ($cmdLinesSet["[$Type]"].contains("CmdLine=$CmdLine") -and
    (-not $Parameters -or $parametersSet["[$Type]"].item($cmdLinesSet["[$Type]"].IndexOf("CmdLine=$CmdLine")) -eq "Parameters=$Parameters")
  ) {
    $index = $cmdLinesSet["[$Type]"].IndexOf("CmdLine=$CmdLine")
    $cmdLinesSet["[$Type]"].RemoveAt($index) | Out-Null
    $parametersSet["[$Type]"].RemoveAt($index) | Out-Null
    $removed = $true
  }

  Set-HPPrivatePSScriptsEntries -CmdLines $cmdLinesSet -Parameters $parametersSet -Path $Path
  return $removed
}

<#
.SYNOPSIS
  Applies BIOS updates using a Windows Update package
 
.DESCRIPTION
  This command extracts the Windows Update file and prepares the system to receive a BIOS update. This command is invoked by the Get-HPBIOSWindowsUpdate command.
 
.PARAMETER WindowsUpdateFile
  Specifies the absolute path to the compressed CAB file downloaded with the Get-HPBIOSWindowsUpdate command.
  The file name must follow the standard: platform family (3 digit) + underscore + BIOS version (6 digits) + .cab, for instance: R70_011200.cab
 
.NOTES
  Requires Windows group policy support
 
.EXAMPLE
  Add-HPBIOSWindowsUpdateScripts -WindowsUpdateFile C:\R70_011200.cab
#>

function Add-HPBIOSWindowsUpdateScripts {
  [CmdletBinding(DefaultParameterSetName = "Default",HelpUri = "https://developers.hp.com/hp-client-management/doc/Add-HPBIOSWindowsUpdateScripts")]
  param(
    [ValidatePattern('^[A-Za-z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[A-Za-z0-9]{3}_[0-9]{6}\.(cab|CAB)$')]
    [Parameter(Mandatory = $true,Position = 0,ParameterSetName = "Default")]
    [string]$WindowsUpdateFile
  )

  $gpt = "${env:SystemRoot}\System32\GroupPolicy\gpt.ini"
  $scripts = "${env:SystemRoot}\System32\GroupPolicy\Machine"

  New-Item -ItemType Directory -Force -Path "$scripts\Scripts" | Out-Null
  New-Item -ItemType Directory -Force -Path "$scripts\Scripts\Startup" | Out-Null
  New-Item -ItemType Directory -Force -Path "$scripts\Scripts\Shutdown" | Out-Null

  Invoke-HPPrivateExpandCAB -cab $WindowsUpdateFile -Verbose:$VerbosePreference

  $fileName = ($WindowsUpdateFile -split '\\')[-1]
  $directory = $WindowsUpdateFile -replace $fileName,''

  $fileName = $fileName.substring(0,$fileName.Length - 4)

  # Directory name comes from WindowsUpdateFile parameter, no need to check if version has 4 or 6 digits
  $expectedDir = "$directory$fileName.cab.dir"

  # File name is expected to include a 6 digit version but will find all inf files
  # that match the version regardless of trailing zeroes
  # in case inf file includes a 4 digit version instead.
  # Using first inf file if mulitple inf files are found.
  $inf = (, $(Get-ChildItem -Path $expectedDir -File -Filter "$($fileName.TrimEnd("0"))*.inf" -Name))[0]

  if (-not $inf) {
    # inf file with 4 digits or 6 digits not found in expanded cab file
    Remove-Item $expectedDir -Force -Recurse
    throw "Invalid cab file, did not find .inf in contents"
  }

  $infFileName = $inf.substring(0,$inf.Length - 4)
  $binFileLength = (Get-Item "$expectedDir\$infFileName.bin").Length

  # Remove cab file
  Remove-Item $WindowsUpdateFile -Force

  # Remove previously extracted files in shutdown script directory
  Remove-Item -Recurse -Force "$scripts\Scripts\Shutdown\wu_image" -ErrorAction Ignore

  # Move cab.dir and its contents to the shutdown script directory
  Move-Item $expectedDir "$scripts\Scripts\Shutdown\wu_image" -Force
  $log = ".\wu_bios_update.log"

  # CMSL modules should be included at startup to use Remove-PSScriptsEntry function
  $clientManagementModulePath = (Get-Module -Name HP.ClientManagement).Path
  $privateModulePath = (Get-Module -Name HP.Private).Path

  # Move DeviceInstall service to be notified after the Group Policy shutdown script
  $preshutdownOrder = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control" -Name "PreshutdownOrder").PreshutdownOrder | Where-Object { $_ -ne "DeviceInstall" }
  $preshutdownOrder += "DeviceInstall"
  Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control" -Name "PreshutdownOrder" -Value $preshutdownOrder -Force -ErrorAction SilentlyContinue | Out-Null

  # Clean EFI partition to better predict free space remaining at the time of reboot
  # There is still a risk that there will not be enough space when pnputil script is executed at reboot if EFI partition is modified
  # between now and reboot time, but this check will at least avoid the case where there is not enough space now.
  # We will notify users of insufficient space now rather than trying to figure out after reboot why bios update failed.
  $volumes = Get-Partition  | Select-Object `
  @{ Name = "Path"; Expression = { (Get-Volume -Partition $_).Path } },`
  @{ Name = "Mount"; Expression = {(Get-Volume -Partition $_).DriveType } },`
  @{ Name = "Type"; Expression = { $_.Type } },`
  @{ Name = "Disk"; Expression = { $_.DiskNumber } },`
  @{ Name = "UniqueId"; Expression = { $_.UniqueId } }
  $volumes = $volumes | Where-Object Mount -EQ "Fixed"
  [array]$efi = $volumes | Where-Object { $_.type -eq "System" }
  [array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).OperationalStatus -eq "Online" }
  [array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).IsBoot -eq $true }
  Remove-Item "$($efi[0].Path)EFI\HP\DEVFW\*" -Recurse -Force -ErrorAction Ignore 

  # Check if there is enough space on EFI partition to apply BIOS update with buffer of 3MB to be conservative
  $freeSpace = Get-Volume -FilePath $efi.Path | Select-Object SizeRemaining
  if ($freeSpace.SizeRemaining -lt ($binFileLength + 3000000)) {
    throw "Not enough space on EFI partition to apply BIOS update. Free space is $($freeSpace.SizeRemaining) bytes, and the update requires $($binFileLength) bytes plus a buffer of 3MB."
  }

  # Startup script
  '$driver = Get-WmiObject Win32_PnPSignedDriver | ? DeviceClass -eq "Firmware" | Where Manufacturer -eq "HP Inc."
$infName = $driver.InfName
if ($infName) {
  Write-Host "INF name: $infName" *>> '
 + $log + '
  '
 + ${env:SystemRoot} + '\System32\pnputil.exe /delete-driver $infName /uninstall /force *>> ' + $log + '
} else {
  Write-Host "No device to clean up" *>> '
 + $log + '
}
 
Write-Host "Clean EFI partition" *>> '
 + $log + '
$volumes = Get-Partition | Select-Object `
  @{ Name = "Path"; Expression = { (Get-Volume -Partition $_).Path } },`
  @{ Name = "Mount"; Expression = {(Get-Volume -Partition $_).DriveType } },`
  @{ Name = "Type"; Expression = { $_.Type } },`
  @{ Name = "Disk"; Expression = { $_.DiskNumber } }
$volumes = $volumes | Where-Object Mount -EQ "Fixed"
[array]$efi = $volumes | Where-Object { $_.type -eq "System" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).OperationalStatus -eq "Online" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).IsBoot -eq $true }
Remove-Item "$($efi[0].Path)EFI\HP\DEVFW\*" -Recurse -Force -ErrorAction Ignore *>> '
 + $log + '
 
$freeSpace = Get-Volume -FilePath $efi.Path | Select-Object SizeRemaining
Write-Host "Free space on EFI partition: $($freeSpace.SizeRemaining)" *>> '
 + $log + '
 
Remove-Item -Force '
 + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Startup\wu_startup.ps1 *>> ' + $log + '
Remove-Item -Force '
 + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Shutdown\wu_shutdown.ps1 *>> ' + $log + '
Remove-Item -Recurse -Force '
 + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Shutdown\wu_image *>> ' + $log + '
 
if (Get-Module -Name HP.Private) {remove-module -force HP.Private }
if (Get-Module -Name HP.ClientManagement) {remove-module -force HP.ClientManagement }
Import-Module -Force '
 + $privateModulePath + ' *>> ' + $log + '
Import-Module -Force '
 + $clientManagementModulePath + ' -Function Remove-PSScriptsEntry *>> ' + $log + '
Remove-PSScriptsEntry -Type "Startup" -CmdLine wu_startup.ps1 *>> '
 + $log + '
Remove-PSScriptsEntry -Type "Shutdown" -CmdLine wu_shutdown.ps1 *>> '
 + $log + '
gpupdate /wait:0 /force /target:computer *>> '
 + $log + '
'
 | Out-File "$scripts\Scripts\Startup\wu_startup.ps1"

  # Shutdown script
  'param($wu_inf_name)
 
net start DeviceInstall *>> '
 + $log + '
$driver = Get-WmiObject Win32_PnPSignedDriver | ? DeviceClass -eq "Firmware" | Where Manufacturer -eq "HP Inc."
$infName = $driver.InfName
if ($infName) {
  Write-Host "INF name: $infName" *>> '
 + $log + '
  '
 + ${env:SystemRoot} + '\System32\pnputil.exe /delete-driver $infName /uninstall /force *>> ' + $log + '
} else {
  Write-Host "No device to clean up" *>> '
 + $log + '
}
 
Write-Host "Clean EFI partition" *>> '
 + $log + '
$volumes = Get-Partition | Select-Object `
  @{ Name = "Path"; Expression = { (Get-Volume -Partition $_).Path } },`
  @{ Name = "Mount"; Expression = {(Get-Volume -Partition $_).DriveType } },`
  @{ Name = "Type"; Expression = { $_.Type } },`
  @{ Name = "Disk"; Expression = { $_.DiskNumber } }
$volumes = $volumes | Where-Object Mount -EQ "Fixed"
[array]$efi = $volumes | Where-Object { $_.type -eq "System" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).OperationalStatus -eq "Online" }
[array]$efi = $efi | Where-Object { (Get-Disk -Number $_.Disk).IsBoot -eq $true }
Remove-Item "$($efi[0].Path)EFI\HP\DEVFW\*" -Recurse -Force -ErrorAction Ignore *>> '
 + $log + '
 
$freeSpace = Get-Volume -FilePath $efi.Path | Select-Object SizeRemaining
Write-Host "Free space on EFI partition: $($freeSpace.SizeRemaining)" *>> '
 + $log + '
 
$volume = Get-BitLockerVolume | Where-Object VolumeType -EQ "OperatingSystem"
if ($volume.ProtectionStatus -ne "Off") {
  Suspend-BitLocker -MountPoint $volume.MountPoint -RebootCount 1 *>> '
 + $log + '
}
 
Write-Host "Invoke PnPUtil to update the BIOS" *>> '
 + $log + '
'
 + ${env:SystemRoot} + '\System32\pnputil.exe /add-driver ' + ${env:SystemRoot} + '\System32\GroupPolicy\Machine\Scripts\Shutdown\wu_image\$wu_inf_name.inf /install *>> ' + $log + '
Write-Host "WU driver installed" *>> '
 + $log + '
 
$volume = Get-BitLockerVolume | Where-Object VolumeType -EQ "OperatingSystem"
if ($volume.ProtectionStatus -ne "Off") {
  Suspend-BitLocker -MountPoint $volume.MountPoint -RebootCount 1 *>> '
 + $log + '
}
'
 | Out-File "$scripts\Scripts\Shutdown\wu_shutdown.ps1"

  "[General]`ngPCMachineExtensionNames=[{42B5FAAE-6536-11D2-AE5A-0000F87571E3}{40B6664F-4972-11D1-A7CA-0000F87571E3}]`nVersion=65537" | Set-Content -Path $gpt -Force

  Remove-PSScriptsEntry -Type "Startup" -CmdLine "wu_startup.ps1" | Out-Null
  Remove-PSScriptsEntry -Type "Shutdown" -CmdLine "wu_shutdown.ps1" | Out-Null
  Add-PSScriptsEntry -Type "Startup" -CmdLine "wu_startup.ps1"
  Add-PSScriptsEntry -Type "Shutdown" -CmdLine "wu_shutdown.ps1" -Parameters "$infFileName"
  gpupdate /wait:0 /force /target:computer
  Write-Host -ForegroundColor Cyan "Firmware image has been deployed. The process will continue after reboot."
}

<#
 .SYNOPSIS
  Retrieves the platform name, system ID, or operating system support using either the platform name or its system ID
 
.DESCRIPTION
  This command retrieves information about the platform, given a platform name or system ID. This command can be used to convert between platform name and system IDs. Note that a platform may have multiple system IDs, or a system ID may map to multiple platforms.
 
  This command returns the following information:
 
  - SystemID: the system ID for this platform
  - FamilyID: the platform family ID
  - Name: the name of the platform
  - DriverPackSupport: this platform supports driver packs
 
  Note that this command is not supported in WinPE.
 
.PARAMETER Platform
  Specifies a platform id (a 4-digit hexadecimal number) for the command to query with
 
.PARAMETER Name
  Specifies a platform name for the command to query with. The name must match the platform name exactly, unless the -Match/-Like parameter is also specified.
 
.PARAMETER Like
  Allows the query to return outputs based on a substring match rather than an exact match. If the platform contains the substring defined by the -Name parameter, it will be included in the output. This parameter can also be specified as -Match, for backwards compatibility.
 
  However, this parameter is now obsolete and may be removed at a future time. You can simply pass wildcards in the name field instead of using the like parameter.
  For example, "Get-HPDeviceDetails -name '\*EliteBook\*'" and "Get-HPDeviceDetails -like -name 'EliteBook'" are identical.
 
.PARAMETER OSList
  If specified, this command returns the list of supported operating systems for the specified platform.
 
.PARAMETER Url
  Specifies an alternate location for the HP Image Assistant (HPIA) platform list XML. This URL must be HTTPS. If not specified, https://hpia.hpcloud.hp.com/ref is used by default.
 
.EXAMPLE
  Get-HPDeviceDetails -Platform 8100
 
.EXAMPLE
  Get-HPDeviceDetails -Name 'HP ProOne 400 G3 20-inch Touch All-in-One PC'
 
.EXAMPLE
  Get-HPDeviceDetails -Like -Name '840 G5'
 
#>

function Get-HPDeviceDetails {
  [CmdletBinding(
    DefaultParameterSetName = "FromID",
    HelpUri = "https://developers.hp.com/hp-client-management/doc/Get-HPDeviceDetails")
  ]
  param(
    [ValidatePattern("^[a-fA-F0-9]{4}$")]
    [Parameter(Mandatory = $false,Position = 0,ParameterSetName = "FromID")]
    [string]$Platform,

    [Parameter(Mandatory = $true,Position = 1,ParameterSetName = "FromName")]
    [string]$Name,

    [Parameter(Mandatory = $false,Position = 2,ParameterSetName = "FromName")]
    [Alias('Match')]
    [switch]$Like,

    [Parameter(Mandatory = $false,ParameterSetName = "FromName")]
    [Parameter(Mandatory = $false,ParameterSetName = "FromID")]
    [switch]$OSList,

    [Parameter(Mandatory = $false,Position = 3,ParameterSetName = "FromName")]
    [Parameter(Mandatory = $false,Position = 1,ParameterSetName = "FromID")]
    [string]$Url = "https://hpia.hpcloud.hp.com/ref"
  )

  if (Test-WinPE -Verbose:$VerbosePreference) { throw "Getting HP Device details is not supported in WinPE" }

    # only allow https or file paths with or without file:// URL prefix
  if ($Url -and -not ($Url.StartsWith("https://",$true,$null) -or [System.IO.Directory]::Exists($Url) -or $Url.StartsWith("file://",$true,$null))) {
    throw [System.ArgumentException]"Only HTTPS or valid existing directory paths are supported."
  }

  $filename = "platformList.cab"
  $Url = "$Url/$filename"
  $try_on_ftp = $false

  try {
    $file = Get-HPPrivateOfflineCacheFiles -url $Url -FileName $filename -Expand -Verbose:$VerbosePreference
  }
  catch {
    # platformList is not reachable on AWS, try to get it from FTP
    $try_on_ftp = $true
  }

  if ($try_on_ftp)
  {
    try {
      $url = "https://ftp.hp.com/pub/caps-softpaq/cmit/imagepal/ref/platformList.cab"
      $file = Get-HPPrivateOfflineCacheFiles -url $url -FileName $filename -Expand -Verbose:$VerbosePreference
    }
    catch {
      Write-Host -ForegroundColor Magenta "platformList is not available on AWS or FTP."
      throw [System.Net.WebException]"Could not find platformList."
    }
  }

  if (-not $platform -and -not $Name) {
    try { $platform = Get-HPDeviceProductID -Verbose:$VerbosePreference }
    catch { Write-Verbose "No platform found." }
  }
  if ($platform) {
    $platform = $platform.ToLower()
  }
  if ($PSCmdlet.ParameterSetName -eq "FromID") {
    $data = Select-Xml -Path "$file" -XPath "/ImagePal/Platform/SystemID[normalize-space(.)=`"$platform`"]/parent::*"
  }
  else {
    $data = Select-Xml -Path "$file" -XPath "/ImagePal/Platform/ProductName[translate(substring(`"$($name.ToLower())`",0), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')]/parent::*"
  }

  if (-not $data) { return }

  $searchName = $Name
  if ($Like.IsPresent)
  {
    if (-not ($searchName).StartsWith('*')) { $searchName = ("*$searchName") }
    if (-not ($searchName).EndsWith('*')) { $searchName = ("$searchName*") }
  }

  $data.Node | ForEach-Object {
    $__ = $_
    $pn = $_.ProductName. "#text"

    if ($oslist.IsPresent) {
      [array]$r = ($__.OS | ForEach-Object {
        if (($PSCmdlet.ParameterSetName -eq "FromID") -or ($pn -like $searchName)) {
          $rid = $Null
          if ("OSReleaseId" -in $_.PSObject.Properties.Name) { $rid = $_.OSReleaseId }

          [string]$osv = $_.OSVersion
          if ("OSReleaseIdDisplay" -in $_.PSObject.Properties.Name -and $_.OSReleaseIdDisplay -ne '20H2') {
            $rid = $_.OSReleaseIdDisplay
          }

          $obj = New-Object -TypeName PSCustomObject -Property @{
            SystemID = $__.SystemID.ToUpper()
            OperatingSystem = $_.OSDescription
            OperatingSystemVersion = $osv
            Architecture = $_.OSArchitecture
          }
          if ($rid) {
            $obj | Add-Member -NotePropertyName OperatingSystemRelease -NotePropertyValue $rid
          }
          if ("OSBuildId" -in $_.PSObject.Properties.Name) {
            $obj | Add-Member -NotePropertyName BuildNumber -NotePropertyValue $_.OSBuildId
          }
          $obj
        }
      })
    }
    else {

      [array]$r = ($__.ProductName | ForEach-Object {
        if (($PSCmdlet.ParameterSetName -eq "FromID") -or ($_. "#text" -like $searchName)) {
          New-Object -TypeName PSCustomObject -Property @{
            SystemID = $__.SystemID.ToUpper()
            Name = $_. "#text"
            DriverPackSupport = [System.Convert]::ToBoolean($_.DPBCompliant)
            UWPDriverPackSupport = [System.Convert]::ToBoolean($_.UDPCompliant)
          }
        }
      })
    }
    return $r
  }
}



function getFormattedBiosSettingValue {
  [CmdletBinding()]
  param($obj)
  switch ($obj.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSString' } {
      $result = $obj.Value

    }
    { $_ -eq 'HPBIOS_BIOSInteger' } {
      $result = $obj.Value
    }
    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      $result = $obj.CurrentValue
    }
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      throw [System.InvalidOperationException]"Password values cannot be retrieved, it will always result in an empty string"
    }
    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      $result = $obj.Value
    }
  }
  return $result
}

function getWmiField ($obj,$fn) { $obj.$fn }


# format a setting using BCU (custom) format
function convertSettingToBCU ($setting) {
  #if ($setting.DisplayInUI -eq 0) { return }
  switch ($setting.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSString' } {
      Write-Output $setting.Name

      if ($setting.Value.contains("`n")) {
        $setting.Value.Split("`n") | ForEach-Object {
          $c = $_.trim()
          Write-Output "`t$c" }
      }
      else {
        Write-Output "`t$($setting.Value)"
      }

    }
    { $_ -eq 'HPBIOS_BIOSInteger' } {
      Write-Output $setting.Name
      Write-Output "`t$($setting.Value)"
    }
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Output $setting.Name
      Write-Output ""
    }
    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      Write-Output $setting.Name
      $fields = $setting.Value.Split(",")
      foreach ($f in $fields) {
        Write-Output "`t$f"
      }
    }
    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      Write-Output $setting.Name
      if ($null -ne $setting.Value) {
        $fields = $setting.Value.Split(",")
        foreach ($f in $fields) {
          Write-Output "`t$f"
        }
      }
      else {
        Write-Output "`t$($setting.Value)"
      }
    }
  }
}

function formatBiosVersionsOutputList ($doc) {
  switch ($format) {
    "json" { return $doc | ConvertTo-Json }
    "xml" {
      Write-Output "<bios id=`"$platform`">"
      if ($all)
      {
        $doc | ForEach-Object { Write-Output "<item><ver>$($_.Ver)</ver><bin>$($_.bin)</bin><date>$($_.date)</date><rollback_allowed>$($_.RollbackAllowed)</rollback_allowed><importance>$($_.Importance)</importance></item>" }
      }
      else {
        $doc | ForEach-Object { Write-Output "<item><ver>$($_.Ver)</ver><bin>$($_.bin)</bin><date>$($_.date)</date></item>" }
      }
      Write-Output "</bios>"
      return
    }
    "csv" {
      return $doc | ConvertTo-Csv -NoTypeInformation
    }
    "list" { $doc | ForEach-Object { Write-Output "$($_.Bin) version $($_.Ver.TrimStart("0")), released $($_.Date)" } }
    default { return $doc }
  }
}


# format a setting using HPIA (xml) format
function convertSettingToXML ($setting) {
  #if ($setting.DIsplayInUI -eq 0) { return }
  Write-Output " <BIOSSetting>"
  Write-Output " <Name>$([System.Web.HttpUtility]::HtmlEncode($setting.Name))</Name>"
  Write-Output " <Class>$($setting.CimClass.CimClassName)</Class>"
  Write-Output " <DisplayInUI>$($setting.DisplayInUI)</DisplayInUI>"
  Write-Output " <IsReadOnly>$($setting.IsReadOnly)</IsReadOnly>"
  Write-Output " <RequiresPhysicalPresence>$($setting.RequiresPhysicalPresence)</RequiresPhysicalPresence>"
  Write-Output " <Sequence>$($setting.Sequence)</Sequence>"

  switch ($setting.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Output " <Value></Value>"
      Write-Output " <Min>$($setting.MinLength)</Min>"
      Write-Output " <Max>$($setting.MaxLength)</Max>"

      Write-Output " <SupportedEncodings Count=""$($setting.SupportedEncoding.Count)"">"
      foreach ($e in $setting.SupportedEncoding) {
        Write-Output " <Encoding>$e</Encoding>"
      }
      Write-Output " </SupportedEncodings>"
    }

    { $_ -eq 'HPBIOS_BIOSString' } {
      Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($setting.Value))</Value>"
      Write-Output " <Min>$($setting.MinLength)</Min>"
      Write-Output " <Max>$($setting.MaxLength)</Max>"
    }

    { $_ -eq 'HPBIOS_BIOSInteger' } {
      Write-Output " <Value>$($setting.Value)</Value>"
      #Write-Output " <DisplayInUI>$($setting.DisplayInUI)</DisplayInUI>"
      Write-Output " <Min>$($setting.LowerBound)</Min>"
      Write-Output " <Max>$($setting.UpperBound)</Max>"
    }

    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($setting.CurrentValue))</Value>"
      Write-Output " <ValueList Count=""$($setting.Size)"">"
      foreach ($e in $setting.PossibleValues) {
        Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($e))</Value>"
      }
      Write-Output " </ValueList>"
    }

    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($setting.Value))</Value>"
      Write-Output " <ValueList Count=""$($setting.Size)"">"
      foreach ($e in $setting.Elements) {
        Write-Output " <Value>$([System.Web.HttpUtility]::HtmlEncode($e))</Value>"
      }
      Write-Output " </ValueList>"
    }
  }
  Write-Output " </BIOSSetting>"
}

function convertSettingToJSON ($original_setting) {

  $setting = $original_setting | Select-Object *

  if ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSInteger") {
    $min = $setting.LowerBound
    $max = $setting.UpperBound
    Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty
    Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty

    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value
  }

  if (($setting.CimClass.CimClassName -eq "HPBIOS_BIOSString") -or ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSPassword")) {
    $min = $setting.MinLength
    $max = $setting.MaxLength
    Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty -Force
    Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty -Force
    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value
  }

  if ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSEnumeration") {
    $min = $setting.Size
    $max = $setting.Size
    #Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty
    #Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty
    $setting.Value = $setting.CurrentValue
    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value,PossibleValues
  }

  if ($setting.CimClass.CimClassName -eq "HPBIOS_BIOSOrderedList") {
    #if Elements is null, initialize it as an empty array else select the first object
    $Elements = $setting.Elements,@() | Select-Object -First 1
    $min = $Elements.Count
    $max = $Elements.Count
    Add-Member -InputObject $setting -Name "Min" -Value $min -MemberType NoteProperty
    Add-Member -InputObject $setting -Name "Max" -Value $max -MemberType NoteProperty
    Add-Member -InputObject $setting -Name "PossibleValues" -Value $Elements -MemberType NoteProperty
    $d = $setting | Select-Object -Property Class,DisplayInUI,InstanceName,IsReadOnly,Min,Max,Name,Path,Prerequisites,PrerequisiteSize,RequiresPhysicalPresence,SecurityLevel,Sequence,Value,Elements
  }



  $d | ConvertTo-Json -Depth 5 | Write-Output
}

# format a setting as a CSV entry
function convertSettingToCSV ($setting) {
  switch ($setting.CimClass.CimClassName) {
    { $_ -eq 'HPBIOS_BIOSEnumeration' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"picklist`",$($setting.RequiresPhysicalPresence),$($setting.Size),$($setting.Size)"
    }
    { $_ -eq 'HPBIOS_BIOSString' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"string`",$($setting.RequiresPhysicalPresence),$($setting.MinLength),$($setting.MaxLength)"
    }
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Output "`"$($setting.Name)`",`"`",$($setting.IsReadOnly),`"password`",$($setting.RequiresPhysicalPresence),$($setting.MinLength),$($setting.MaxLength)"
    }
    { $_ -eq 'HPBIOS_BIOSInteger' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"integer`",$($setting.RequiresPhysicalPresence),$($setting.LowerBound),$($setting.UpperBound)"
    }
    { $_ -eq 'HPBIOS_BIOSOrderedList' } {
      Write-Output "`"$($setting.Name)`",`"$($setting.value)`",$($setting.IsReadOnly),`"orderedlist`",$($setting.RequiresPhysicalPresence),$($setting.Size),$($setting.Size)"
    }
  }
}

function extractBIOSVersion {
  [CmdletBinding()]
  param
  (
    [Parameter(Position = 0,Mandatory = $true)]
    [ValidateNotNullOrEmpty()]
    [string]$BIOSVersion

  )
  [string]$ver = $null

  # Does the BIOS version string contains x.xx[.xx]?
  [bool]$found = $BIOSVersion -match '(\d+(\.\d+){1,2})'
  if ($found) {
    $ver = $matches[1]
    Write-Verbose "BIOS version extracted=[$ver]"
  }

  $ver
}




# SIG # Begin signature block
# MIIoGAYJKoZIhvcNAQcCoIIoCTCCKAUCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAFZ1vpy6801Vcp
# j2ISXErv8CfXukR6e/ZJZp70t7Qa5aCCDYowggawMIIEmKADAgECAhAIrUCyYNKc
# TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z
# NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0
# JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr
# Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF
# LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F
# LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh
# 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ
# wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay
# g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI
# YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp
# QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro
# OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB
# WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+
# YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P
# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC
# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v
# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED
# MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql
# +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF
# UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h
# mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw
# YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld
# AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw
# 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP
# LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE
# QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn
# KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji
# WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq
# yK+p/pQd52MbOoZWeE4wggbSMIIEuqADAgECAhAJvPMqSNxAYhV5FFpsbzOhMA0G
# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg
# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjQwMjE1MDAwMDAwWhcNMjUwMjE4
# MjM1OTU5WjBaMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAG
# A1UEBxMJUGFsbyBBbHRvMRAwDgYDVQQKEwdIUCBJbmMuMRAwDgYDVQQDEwdIUCBJ
# bmMuMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEApbF6fMFy6zhGVra3
# SZN418Cp2O8kjihQCU9tqPO9tkzbMyTsgveLJVnXPJNG9kQPMGUNp+wEHcoUzlRc
# YJMEL9fhfzpWPeSIIezGLPCdrkMmS3fdRUwFqEs7z/C6Ui2ZqMaKhKjBJTIWnipe
# rRfzGB7RoLepQcgqeF5s0DBy4oG83dqcRHo3IJRTBg39tHe3mD5uoGHn5n366abX
# vC+k53BVyD8w8XLppFVH5XuNlXMq/Ohf613i7DRb/+u92ZiAPVPXXnlxUE26cuDb
# OfJKN/bXPmvnWcNW3YHVp9ztPTQZhX4yWYXHrAI2Cv6HxUpO6NzhFoRoBTkcYNbA
# 91pf1Vagh/MNcA2BfQYT975/Vlvj9cfEZ/NwZthZuHa3rdrvCKhhjw7YU2QUeaTJ
# 0uaX4g6B9PFNqAASYLach3CDJiLmYEfus/utPh57mk0q27yL25fXo/PaMDXiDNIi
# 7Wuz7A+sPsbtdiY8zvEIRQ+XJXtKAlD4tqG9YzlTO6ZoQX/rAgMBAAGjggIDMIIB
# /zAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4EFgQURH4F
# u5yEAuElYWUbyGRYkNLLrA8wPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEF
# BQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB/wQEAwIH
# gDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRw
# Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hB
# Mzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNB
# NDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQELBQAD
# ggIBAFiCyuI6qmaQodDyMNpp0l7eIXFgJ4JI59o59PleFj4rcyd/+F4iI7u5if8G
# rV5Kn3s3tK9vfJO8SpqtEh7lL4e69z6v3ohcy4uy2hsjKQ/fFcDo9pQYDGmDVjCa
# D5qSVEIBlJHBe5NKEJAgUE0kaMjLzbi2+8DKJlNtvZ+hatuPl9fMnmU+VbQh7JhZ
# yJdz8Ay0tcQ9lC8HAX5Ah/pU+Vtv+c8gMSxjS1aWXoGCa1869IVi2O6qx7MuX12U
# 1eIpB9XxYr7HSebvg2G7Gz6nCh7u+4k7m3hJu9EStUIN2JII5260+E60uDWoHEhx
# tHbdueFQxJrTKnhplOSaaPFCVBDkWG83ZzN9N3z/45w1pBUNBiPJdRQJ58MhBYQe
# Zl90heMBL8QNQk2i0E5gHNT9pJiCR9+mvJkRxEVgUn+16ZpVnI6kzhThV9qBaWVF
# h83X4UWc/nwHKIuu+4x4fmkYc79A3MrsHflZIO8jOy0GC/xBnZTQ8s5b9Tb2UkHk
# w692Ypl7War3W7M37JCAPC/A7M4CwQYjdjG43zs5m36auYVaTvRLKtZVLzcj8oZX
# 4vqhlZ8+jCPXFiuDfoBXiTckTLpv/eHQ6q7Aoda+qARWPPE1U2v5r/lpKVqIx7B4
# PdFZAUf5MtG/Bj7LVXvXjW8ABIJv7L4cI2akn6Es0dmvd6PsMYIZ5DCCGeACAQEw
# fTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNV
# BAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hB
# Mzg0IDIwMjEgQ0ExAhAJvPMqSNxAYhV5FFpsbzOhMA0GCWCGSAFlAwQCAQUAoHww
# EAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK
# KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIIzEqPkB
# fZ74NqIfwDol59VYkLox5ol1yCtMLWjjGyiKMA0GCSqGSIb3DQEBAQUABIIBgFup
# UTVBDYWwvhepEvDfYGQSm//awO8mIO9PRSmf3ztPQzidg+nAMPAVN8o2pT/l2R1i
# vopHxIv8rG/WF2qs3dTcBl//q2FDCowV9j6cA6ZgO/+eLZKly1OCFdch1j0SFxDf
# 05JDZOnGPEwND96ofQ/+wcSfXLpbpdL3zTLbW93DHoMzcL5cUIpfZ5rilAmS8nEt
# EfCoPw+MBONsuAwXAckdEjNZuUBffK2NyIJa8zJTWcbpjzhGaVH5GwCH50RPjV2W
# wlGiFJ9jMNuPzzPvrw9K7BuC6jGi8NevoACcRDL8Xsm/LL5LC68B69949q4QPnFQ
# yxVXrLjnwNQE25fKPv1FVTqc/SiYCyGik7Rw/3TFQ/k7e4jCouWQ1sDBCnWTCfDC
# A58r0ylpL6EdosMhXlDyE63ECqAXWFPwNuO8Ep0f4H09nrIf76DHI6HlaE9gYLmI
# AjQmhH/79PBjOCpyxdfbMcnmRn6bDXWigJ0RL9dz/YRFHRbSwp9E9pdzVqN1haGC
# Fzowghc2BgorBgEEAYI3AwMBMYIXJjCCFyIGCSqGSIb3DQEHAqCCFxMwghcPAgED
# MQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQAQSgaQRnMGUCAQEGCWCGSAGG
# /WwHATAxMA0GCWCGSAFlAwQCAQUABCDP/8vK5IcBq/GKrTNhdZBctEt8fHQ+Mb6C
# uw+YW/2uOgIRAKCg0swIJ45a9SKukFE3f24YDzIwMjQxMTA2MTcxNzMxWqCCEwMw
# gga8MIIEpKADAgECAhALrma8Wrp/lYfG+ekE4zMEMA0GCSqGSIb3DQEBCwUAMGMx
# CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy
# RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg
# Q0EwHhcNMjQwOTI2MDAwMDAwWhcNMzUxMTI1MjM1OTU5WjBCMQswCQYDVQQGEwJV
# UzERMA8GA1UEChMIRGlnaUNlcnQxIDAeBgNVBAMTF0RpZ2lDZXJ0IFRpbWVzdGFt
# cCAyMDI0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvmpzn/aVIauW
# MLpbbeZZo7Xo/ZEfGMSIO2qZ46XB/QowIEMSvgjEdEZ3v4vrrTHleW1JWGErrjOL
# 0J4L0HqVR1czSzvUQ5xF7z4IQmn7dHY7yijvoQ7ujm0u6yXF2v1CrzZopykD07/9
# fpAT4BxpT9vJoJqAsP8YuhRvflJ9YeHjes4fduksTHulntq9WelRWY++TFPxzZrb
# ILRYynyEy7rS1lHQKFpXvo2GePfsMRhNf1F41nyEg5h7iOXv+vjX0K8RhUisfqw3
# TTLHj1uhS66YX2LZPxS4oaf33rp9HlfqSBePejlYeEdU740GKQM7SaVSH3TbBL8R
# 6HwX9QVpGnXPlKdE4fBIn5BBFnV+KwPxRNUNK6lYk2y1WSKour4hJN0SMkoaNV8h
# yyADiX1xuTxKaXN12HgR+8WulU2d6zhzXomJ2PleI9V2yfmfXSPGYanGgxzqI+Sh
# oOGLomMd3mJt92nm7Mheng/TBeSA2z4I78JpwGpTRHiT7yHqBiV2ngUIyCtd0pZ8
# zg3S7bk4QC4RrcnKJ3FbjyPAGogmoiZ33c1HG93Vp6lJ415ERcC7bFQMRbxqrMVA
# Niav1k425zYyFMyLNyE1QulQSgDpW9rtvVcIH7WvG9sqYup9j8z9J1XqbBZPJ5XL
# ln8mS8wWmdDLnBHXgYly/p1DhoQo5fkCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQE
# AwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1Ud
# IAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUv
# cyl2mi91jGogj57IbzAdBgNVHQ4EFgQUn1csA3cOKBWQZqVjXu5Pkh92oFswWgYD
# VR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYB
# BQUHAQEEgYMwgYAwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv
# bTBYBggrBgEFBQcwAoZMaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0VHJ1c3RlZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkq
# hkiG9w0BAQsFAAOCAgEAPa0eH3aZW+M4hBJH2UOR9hHbm04IHdEoT8/T3HuBSyZe
# q3jSi5GXeWP7xCKhVireKCnCs+8GZl2uVYFvQe+pPTScVJeCZSsMo1JCoZN2mMew
# /L4tpqVNbSpWO9QGFwfMEy60HofN6V51sMLMXNTLfhVqs+e8haupWiArSozyAmGH
# /6oMQAh078qRh6wvJNU6gnh5OruCP1QUAvVSu4kqVOcJVozZR5RRb/zPd++PGE3q
# F1P3xWvYViUJLsxtvge/mzA75oBfFZSbdakHJe2BVDGIGVNVjOp8sNt70+kEoMF+
# T6tptMUNlehSR7vM+C13v9+9ZOUKzfRUAYSyyEmYtsnpltD/GWX8eM70ls1V6QG/
# ZOB6b6Yum1HvIiulqJ1Elesj5TMHq8CWT/xrW7twipXTJ5/i5pkU5E16RSBAdOp1
# 2aw8IQhhA/vEbFkEiF2abhuFixUDobZaA0VhqAsMHOmaT3XThZDNi5U2zHKhUs5u
# HHdG6BoQau75KiNbh0c+hatSF+02kULkftARjsyEpHKsF7u5zKRbt5oK5YGwFvgc
# 4pEVUNytmB3BpIiowOIIuDgP5M9WArHYSAR16gc0dP2XdkMEP5eBsX7bf/MGN4K3
# HP50v/01ZHo/Z5lGLvNwQ7XHBx1yomzLP8lx4Q1zZKDyHcp4VQJLu2kWTsKsOqQw
# ggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVT
# MRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1
# c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqG
# SIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbS
# g9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9
# /UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXn
# HwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0
# VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4f
# sbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40Nj
# gHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0
# QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvv
# mz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T
# /jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk
# 42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5r
# mQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
# FgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5n
# P+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcG
# CCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu
# Y29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln
# aUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNV
# HSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIB
# AH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxp
# wc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIl
# zpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQ
# cAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfe
# Kuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+j
# Sbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJsh
# IUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6
# OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDw
# N7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR
# 81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2
# VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQ
# DpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAx
# MDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM
# RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQD
# ExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
# DwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aa
# za57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllV
# cq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT
# +CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd
# 463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+
# EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92k
# J7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5j
# rubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7
# f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJU
# KSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+wh
# X8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQAB
# o4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5n
# P+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0P
# AQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29j
# c3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDww
# OqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ
# RFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IB
# AQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229
# GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FD
# RJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVG
# amlUsLihVo7spNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCw
# rFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvR
# XKwYw02fc7cBqZ9Xql4o4rmUMYIDdjCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEX
# MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0
# ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhALrma8Wrp/lYfG
# +ekE4zMEMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B
# CRABBDAcBgkqhkiG9w0BCQUxDxcNMjQxMTA2MTcxNzMxWjArBgsqhkiG9w0BCRAC
# DDEcMBowGDAWBBTb04XuYtvSPnvk9nFIUIck1YZbRTAvBgkqhkiG9w0BCQQxIgQg
# CkReSbJCyuH25qzFFLgidN+BqZ5IRQ//MPKpfYFGTiYwNwYLKoZIhvcNAQkQAi8x
# KDAmMCQwIgQgdnafqPJjLx9DCzojMK7WVnX+13PbBdZluQWTmEOPmtswDQYJKoZI
# hvcNAQEBBQAEggIAqXwl+2iCQ399YTqMmhbb1KUQtHtmzq3iJIlCiDs1P/2l4aSX
# fYUemiwit4CpZRJqbBHTpWSfys/Y2XJmYt/ICVvi7VVuNjoMabjC3YjNxJv6RrKG
# OiG6UixNBV0mvRUoOzdjr3lUSO9UQBbJu783XQj+88TGOCwCCtyZzxBoZtp6/iwF
# OYxTaHYQlnWBM672UZx0lN4c73wskFyBwU8W6Q5p/JanVEIK7+U1JLCXJHdl0ymY
# UB84/+E9Uoa1GCTvFG8BnPumPaZciHmTERWTKGSd9yGDfMM5KILucntsGbGuDV54
# jEQel9VpiqfTlUTxhOUjt3ktgJVzLuyE8/f4jgCJRZlK3d/87b3jsO5UGZutDbxb
# x9UeXHWW8JFqA9mpZ8lnV1SSgNh5eUwT03Jnxn5tRmaCZ9KCz8MW9qNe0To4C2dZ
# VnGBn84SzDIbz8pdo1zv+9aVx65y9MLnCr5cdUMQpD6g3J6Hr+/vDgPSiNX3K+Cb
# 5WqadVeh7lu3DdZLKL+beOrBUUM7Z+wManOuETCD1r+hi0BmY+y33k004XrmI5jt
# YL7xuo46lS95U02+7uXPtUzjfKshVdiFWp4I+655YiXdb/uaX60fYhuiE2sFUw87
# I2aCdvcK6JTIsZqeBTT5e6twh5QCgnL3LuHVUpqUt92wmB+4dyyJfLCdo8U=
# SIG # End signature block