UnifiStockTracker.psm1

function Write-Color { 
    <#
    .SYNOPSIS
    Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options.
 
    .DESCRIPTION
    Write-Color is a wrapper around Write-Host delivering a lot of additional features for easier color options.
 
    It provides:
    - Easy manipulation of colors,
    - Logging output to file (log)
    - Nice formatting options out of the box.
    - Ability to use aliases for parameters
 
    .PARAMETER Text
    Text to display on screen and write to log file if specified.
    Accepts an array of strings.
 
    .PARAMETER Color
    Color of the text. Accepts an array of colors. If more than one color is specified it will loop through colors for each string.
    If there are more strings than colors it will start from the beginning.
    Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White
 
    .PARAMETER BackGroundColor
    Color of the background. Accepts an array of colors. If more than one color is specified it will loop through colors for each string.
    If there are more strings than colors it will start from the beginning.
    Available colors are: Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White
 
    .PARAMETER StartTab
    Number of tabs to add before text. Default is 0.
 
    .PARAMETER LinesBefore
    Number of empty lines before text. Default is 0.
 
    .PARAMETER LinesAfter
    Number of empty lines after text. Default is 0.
 
    .PARAMETER StartSpaces
    Number of spaces to add before text. Default is 0.
 
    .PARAMETER LogFile
    Path to log file. If not specified no log file will be created.
 
    .PARAMETER DateTimeFormat
    Custom date and time format string. Default is yyyy-MM-dd HH:mm:ss
 
    .PARAMETER LogTime
    If set to $true it will add time to log file. Default is $true.
 
    .PARAMETER LogRetry
    Number of retries to write to log file, in case it can't write to it for some reason, before skipping. Default is 2.
 
    .PARAMETER Encoding
    Encoding of the log file. Default is Unicode.
 
    .PARAMETER ShowTime
    Switch to add time to console output. Default is not set.
 
    .PARAMETER NoNewLine
    Switch to not add new line at the end of the output. Default is not set.
 
    .PARAMETER NoConsoleOutput
    Switch to not output to console. Default all output goes to console.
 
    .EXAMPLE
    Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                      "followed by red ",
                      "and then we have Magenta... ",
                      "isn't it fun? ",
                      "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                      "followed by red ",
                      "and then we have Magenta... ",
                      "isn't it fun? ",
                      "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1
 
    .EXAMPLE
    Write-Color "1. ", "Option 1" -Color Yellow, Green
    Write-Color "2. ", "Option 2" -Color Yellow, Green
    Write-Color "3. ", "Option 3" -Color Yellow, Green
    Write-Color "4. ", "Option 4" -Color Yellow, Green
    Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1
 
    .EXAMPLE
    Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss"
    Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt"
 
    .EXAMPLE
    Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow
    Write-Color -t "my text" -c yellow -b green
    Write-Color -text "my text" -c red
 
    .EXAMPLE
    Write-Color -Text "Testuję czy się ładnie zapisze, czy będą problemy" -Encoding unicode -LogFile 'C:\temp\testinggg.txt' -Color Red -NoConsoleOutput
 
    .NOTES
    Understanding Custom date and time format strings: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
    Project support: https://github.com/EvotecIT/PSWriteColor
    Original idea: Josh (https://stackoverflow.com/users/81769/josh)
 
    #>

    [alias('Write-Colour')]
    [CmdletBinding()]
    param (
        [alias ('T')] [String[]]$Text,
        [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White,
        [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null,
        [alias ('Indent')][int] $StartTab = 0,
        [int] $LinesBefore = 0,
        [int] $LinesAfter = 0,
        [int] $StartSpaces = 0,
        [alias ('L')] [string] $LogFile = '',
        [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss',
        [alias ('LogTimeStamp')][bool] $LogTime = $true,
        [int] $LogRetry = 2,
        [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode',
        [switch] $ShowTime,
        [switch] $NoNewLine,
        [alias('HideConsole')][switch] $NoConsoleOutput
    )
    if (-not $NoConsoleOutput) {
        $DefaultColor = $Color[0]
        if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) {
            Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated."
            return
        }
        if ($LinesBefore -ne 0) {
            for ($i = 0; $i -lt $LinesBefore; $i++) {
                Write-Host -Object "`n" -NoNewline 
            } 
        } # Add empty line before
        if ($StartTab -ne 0) {
            for ($i = 0; $i -lt $StartTab; $i++) {
                Write-Host -Object "`t" -NoNewline 
            } 
        }  # Add TABS before text
        if ($StartSpaces -ne 0) {
            for ($i = 0; $i -lt $StartSpaces; $i++) {
                Write-Host -Object ' ' -NoNewline 
            } 
        }  # Add SPACES before text
        if ($ShowTime) {
            Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))] " -NoNewline 
        } # Add Time before output
        if ($Text.Count -ne 0) {
            if ($Color.Count -ge $Text.Count) {
                # the real deal coloring
                if ($null -eq $BackGroundColor) {
                    for ($i = 0; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline 
                    }
                } else {
                    for ($i = 0; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline 
                    }
                }
            } else {
                if ($null -eq $BackGroundColor) {
                    for ($i = 0; $i -lt $Color.Length ; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewline 
                    }
                    for ($i = $Color.Length; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewline 
                    }
                } else {
                    for ($i = 0; $i -lt $Color.Length ; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewline 
                    }
                    for ($i = $Color.Length; $i -lt $Text.Length; $i++) {
                        Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewline 
                    }
                }
            }
        }
        if ($NoNewLine -eq $true) {
            Write-Host -NoNewline 
        } else {
            Write-Host 
        } # Support for no new line
        if ($LinesAfter -ne 0) {
            for ($i = 0; $i -lt $LinesAfter; $i++) {
                Write-Host -Object "`n" -NoNewline 
            } 
        }  # Add empty line after
    }
    if ($Text.Count -and $LogFile) {
        # Save to file
        $TextToFile = ""
        for ($i = 0; $i -lt $Text.Length; $i++) {
            $TextToFile += $Text[$i]
        }
        $Saved = $false
        $Retry = 0
        Do {
            $Retry++
            try {
                if ($LogTime) {
                    "[$([datetime]::Now.ToString($DateTimeFormat))] $TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false
                } else {
                    "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop -WhatIf:$false
                }
                $Saved = $true
            } catch {
                if ($Saved -eq $false -and $Retry -eq $LogRetry) {
                    Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Tried ($Retry/$LogRetry))"
                } else {
                    Write-Warning "Write-Color - Couldn't write to log file $($_.Exception.Message). Retrying... ($Retry/$LogRetry)"
                }
            }
        } Until ($Saved -eq $true -or $Retry -ge $LogRetry)
    }
}
function Get-UnifiStock {
    <#
    .SYNOPSIS
    Get the stock status of Ubiquiti products from their online store.
 
    .DESCRIPTION
    Get the stock status of Ubiquiti products from their online store.
 
    .PARAMETER Store
    The store to check for stock. Valid values are: Europe, USA, UK
    If you want to use a different store you can use Get-UnifiStockLegacy for other countries.
    This is because the legacy store has a different format for the JSON data, and are not yet migrated to new "look"
 
    .PARAMETER Collection
    Which collection to list.
 
    .EXAMPLE
    Get-UnifiStock -Store USA -Collection AccessoriesCabling, CableBox | Sort-Object -Property Name | Format-Table
 
        .EXAMPLE
    Get-UnifiStock -Store Europe -Collection HostingAndGatewaysCloud,DreamMachine, DreamRouter | Sort-Object -Property Name | Format-Table
 
    .EXAMPLE
    Get-UnifiStock -Store Europe | Sort-Object -Property Name | Format-Table
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [ValidateSet('Europe', 'USA', 'UK')]
        [Parameter(Mandatory)][string] $Store,
        [ValidateSet(
            'AccessoriesCabling',
            'AccessPointMounting',
            'AccessPointSkins',
            'CableBox',
            'CablePatch',
            'CableSFP',
            'CameraEnhancers',
            'CameraSecurityBulletDSLR',
            'CameraSecurityBulletHighPerformance',
            'CameraSecurityBulletStandard',
            'CameraSecurityCompactPoEWired',
            'CameraSecurityCompactWiFiConnected',
            'CameraSecurityDome360',
            'CameraSecurityDomeSlim',
            'CameraSecurityDoorAccessAccessories',
            'CameraSecurityDoorAccessReaders',
            'CameraSecurityDoorAccessStarterKit',
            'CameraSecurityInteriorDesign',
            'CameraSecurityNVRLargeScale',
            'CameraSecurityNVRMidScale',
            'CameraSecurityPTZ',
            'CameraSecuritySpecialChime',
            'CameraSecuritySpecialSensor',
            'CameraSecuritySpecialViewport',
            'CameraSecuritySpecialWiFiDoorbell',
            'CameraSkins',
            'DesktopStands',
            'DeviceMounting',
            'DreamMachine',
            'DreamRouter',
            'HDDStorage',
            'HostingAndGatewaysCloud',
            'HostingAndGatewaysLargeScale',
            'HostingAndGatewaysSmallScale',
            'InstallationsRackmount',
            'InternetBackup',
            'NewIntegrationsAVDisplayMounting',
            'NewIntegrationsAVGiantPoETouchscreens',
            'NewIntegrationsPhoneATA',
            'NewIntegrationsPhoneCompact',
            'NewIntegrationsPhoneExecutive',
            'PoEAndPower',
            'PoEPower',
            'PowerTechPowerRedundancy',
            'PowerTechUninterruptiblePoE',
            'SwitchingEnterpriseAggregation',
            'SwitchingEnterprisePoE',
            'SwitchingProEthernet',
            'SwitchingProPoE',
            'SwitchingStandardEthernet',
            'SwitchingStandardPoE',
            'SwitchingUtility10GbpsEthernet',
            'SwitchingUtilityMini',
            'SwitchingUtilityPoE',
            'WiFiBuildingBridge10Gigabit',
            'WiFiFlagshipCompact',
            'WiFiFlagshipHighCapacity',
            'WiFiInWallOutletMesh',
            'WiFiMan',
            'WiFiOutdoorFlexible',
            'WiFiFlagshipLongRange',
            'WiFiOutdoorLongRange',
            "NewIntegrationsMobileRouting",
            "CameraSecurityDoorAccessHub",
            "NewIntegrationsAVDigitalSignage",
            "SwitchingUtilityIndustrial",
            "SwitchingUtilityIndoorOutdoor",
            "SwitchingEnterprise10GbpsEthernet",
            "SwitchingUtilityHiPowerPoE",
            "WiFiMegaCapacity",
            "PowerTechUninterruptiblePower",
            "PowerTechPowerDistribution",
            "CameraSecuritySpecialFloodlight",
            "DreamWall",
            "NewIntegrationsEVCharging",
            "CloudKeyRackMount",
            "AmpliFiMesh",
            "AmpliFiAlien",
            "WiFiInWallCompact",
            "WiFiInWallHighCapacity",
            "AccessPointAntennas",
            "CameraSecurityBulletEnhancedAI",
            "WiFiBuildingBridgeGigabit",
            # organizational collections
            "Cabling", "AccessPointMounting", "AccessPointSkins",
            "CableSFP", "CameraEnhancers", "CameraSkins", "DeviceMounting", "DisplayMounting",
            "HostingAndGatewaysCloud", "HostingAndGatewaysLargeScale", "HostingAndGatewaysSmallScale",
            "PoEAndPower", "PoEPower", "WiFiManager", "InternetBackup", "PowerRedundancy",
            "ProEthernetSwitching", "StandardEthernetSwitching", "StandardPoESwitching",
            "10GbpsEthernetSwitching", "PoESwitching",
            "FlagshipCompactWiFi", "FlagshipHighCapacityWiFi", "InWallHighCapacityWiFi", "OutdoorFlexibleWiFi", "UICare"
        )][string[]] $Collection
    )

    $Stores = @{
        Europe = 'eu'
        USA    = 'us'
        UK     = 'uk'
    }

    $StoreLinks = @{
        Europe = 'https://eu.store.ui.com/eu/en'
        USA    = 'https://store.ui.com/us/en'
        UK     = 'https://uk.store.ui.com/uk/en'
    }

    $Accessories = @{
        # direct collections
        "uisp-accessories-cabling"                              = "AccessoriesCabling"
        "unifi-accessory-tech-access-point-mounting"            = "AccessPointMounting"
        "unifi-accessory-tech-access-point-skins"               = "AccessPointSkins"
        "unifi-accessory-tech-cable-box"                        = "CableBox"
        "unifi-accessory-tech-cable-patch"                      = "CablePatch"
        "unifi-accessory-tech-cable-sfp"                        = "CableSFP"
        "unifi-accessory-tech-camera-enhancers"                 = "CameraEnhancers"
        "unifi-accessory-tech-camera-skins"                     = "CameraSkins"
        "unifi-accessory-tech-desktop-stands"                   = "DesktopStands"
        "unifi-accessory-tech-device-mounting"                  = "DeviceMounting"
        "unifi-accessory-tech-hdd-storage"                      = "HDDStorage"
        "unifi-accessory-tech-hosting-and-gateways-cloud"       = "HostingAndGatewaysCloud"
        "unifi-accessory-tech-hosting-and-gateways-large-scale" = "HostingAndGatewaysLargeScale"
        "unifi-accessory-tech-hosting-and-gateways-small-scale" = "HostingAndGatewaysSmallScale"
        "unifi-accessory-tech-installations-rackmount"          = "InstallationsRackmount"
        "unifi-accessory-tech-poe-and-power"                    = "PoEAndPower"
        "unifi-accessory-tech-poe-power"                        = "PoEPower"
        "unifi-accessory-tech-wifiman"                          = "WiFiMan"
        "unifi-camera-security-bullet-dslr"                     = "CameraSecurityBulletDSLR"
        "unifi-camera-security-bullet-high-performance"         = "CameraSecurityBulletHighPerformance"
        "unifi-camera-security-bullet-standard"                 = "CameraSecurityBulletStandard"
        "unifi-camera-security-compact-poe-wired"               = "CameraSecurityCompactPoEWired"
        "unifi-camera-security-compact-wifi-connected"          = "CameraSecurityCompactWiFiConnected"
        "unifi-camera-security-dome-360"                        = "CameraSecurityDome360"
        "unifi-camera-security-dome-slim"                       = "CameraSecurityDomeSlim"
        "unifi-camera-security-door-access-accessories"         = "CameraSecurityDoorAccessAccessories"
        "unifi-camera-security-door-access-readers"             = "CameraSecurityDoorAccessReaders"
        "unifi-camera-security-door-access-starter-kit"         = "CameraSecurityDoorAccessStarterKit"
        "unifi-camera-security-interior-design"                 = "CameraSecurityInteriorDesign"
        "unifi-camera-security-nvr-large-scale"                 = "CameraSecurityNVRLargeScale"
        "unifi-camera-security-nvr-mid-scale"                   = "CameraSecurityNVRMidScale"
        "unifi-camera-security-ptz"                             = "CameraSecurityPTZ"
        "unifi-camera-security-special-chime"                   = "CameraSecuritySpecialChime"
        "unifi-camera-security-special-sensor"                  = "CameraSecuritySpecialSensor"
        "unifi-camera-security-special-viewport"                = "CameraSecuritySpecialViewport"
        "unifi-camera-security-special-wifi-doorbell"           = "CameraSecuritySpecialWiFiDoorbell"
        "unifi-dream-machine"                                   = "DreamMachine"
        "unifi-dream-router"                                    = "DreamRouter"
        "unifi-internet-backup"                                 = "InternetBackup"
        "unifi-new-integrations-av-display-mounting"            = "NewIntegrationsAVDisplayMounting"
        "unifi-new-integrations-av-giant-poe-touchscreens"      = "NewIntegrationsAVGiantPoETouchscreens"
        "unifi-new-integrations-phone-ata"                      = "NewIntegrationsPhoneATA"
        "unifi-new-integrations-phone-compact"                  = "NewIntegrationsPhoneCompact"
        "unifi-new-integrations-phone-executive"                = "NewIntegrationsPhoneExecutive"
        "unifi-power-tech-power-redundancy"                     = "PowerTechPowerRedundancy"
        "unifi-power-tech-uninterruptible-poe"                  = "PowerTechUninterruptiblePoE"
        "unifi-switching-enterprise-aggregation"                = "SwitchingEnterpriseAggregation"
        "unifi-switching-enterprise-power-over-ethernet"        = "SwitchingEnterprisePoE"
        "unifi-switching-pro-ethernet"                          = "SwitchingProEthernet"
        "unifi-switching-pro-power-over-ethernet"               = "SwitchingProPoE"
        "unifi-switching-standard-ethernet"                     = "SwitchingStandardEthernet"
        "unifi-switching-standard-power-over-ethernet"          = "SwitchingStandardPoE"
        "unifi-switching-utility-10-gbps-ethernet"              = "SwitchingUtility10GbpsEthernet"
        "unifi-switching-utility-mini"                          = "SwitchingUtilityMini"
        "unifi-switching-utility-poe"                           = "SwitchingUtilityPoE"
        "unifi-wifi-building-bridge-10-gigabit"                 = "WiFiBuildingBridge10Gigabit"
        "unifi-wifi-flagship-compact"                           = "WiFiFlagshipCompact"
        "unifi-wifi-flagship-high-capacity"                     = "WiFiFlagshipHighCapacity"
        "unifi-wifi-inwall-outlet-mesh"                         = "WiFiInWallOutletMesh"
        "unifi-wifi-outdoor-flexible"                           = "WiFiOutdoorFlexible"
        "unifi-wifi-flagship-long-range"                        = "WiFiFlagshipLongRange"
        "unifi-wifi-outdoor-long-range"                         = "WiFiOutdoorLongRange"

        "unifi-new-integrations-mobile-routing"                 = "NewIntegrationsMobileRouting"
        "unifi-camera-security-door-access-hub"                 = "CameraSecurityDoorAccessHub"
        "unifi-new-integrations-av-digital-signage"             = "NewIntegrationsAVDigitalSignage"
        "unifi-switching-utility-industrial"                    = "SwitchingUtilityIndustrial"
        "unifi-switching-utility-indoor-outdoor"                = "SwitchingUtilityIndoorOutdoor"
        "unifi-switching-enterprise-10-gbps-ethernet"           = "SwitchingEnterprise10GbpsEthernet"
        "unifi-switching-utility-hi-power-poe"                  = "SwitchingUtilityHiPowerPoE"
        "unifi-wifi-mega-capacity"                              = "WiFiMegaCapacity"
        "unifi-power-tech-uninterruptible-power"                = "PowerTechUninterruptiblePower"
        "unifi-power-tech-power-distribution"                   = "PowerTechPowerDistribution"
        "unifi-camera-security-special-floodlight"              = "CameraSecuritySpecialFloodlight"
        "unifi-dream-wall"                                      = "DreamWall"
        "unifi-new-integrations-ev-charging"                    = "NewIntegrationsEVCharging"
        "cloud-key-rack-mount"                                  = "CloudKeyRackMount"
        "amplifi-mesh"                                          = "AmpliFiMesh"
        "amplifi-alien"                                         = "AmpliFiAlien"
        "unifi-wifi-inwall-compact"                             = "WiFiInWallCompact"
        "unifi-wifi-inwall-high-capacity"                       = "WiFiInWallHighCapacity"
        "unifi-accessory-tech-access-point-antennas"            = "AccessPointAntennas"
        "unifi-camera-security-bullet-enhanced-ai"              = "CameraSecurityBulletEnhancedAI"
        "unifi-wifi-building-bridge-gigabit"                    = "WiFiBuildingBridgeGigabit"

        # organizational collection
        "accessories-cabling"                                   = "Cabling"
        "accessory-tech-access-point-mounting"                  = "AccessPointMounting"
        "accessory-tech-access-point-skins"                     = "AccessPointSkins"
        "accessory-tech-cable-sfp"                              = "CableSFP"
        "accessory-tech-camera-enhancers"                       = "CameraEnhancers"
        "accessory-tech-camera-skins"                           = "CameraSkins"
        "accessory-tech-device-mounting"                        = "DeviceMounting"
        "accessory-tech-display-mounting"                       = "DisplayMounting"
        "accessory-tech-hosting-and-gateways-cloud"             = "HostingAndGatewaysCloud"
        "accessory-tech-hosting-and-gateways-large-scale"       = "HostingAndGatewaysLargeScale"
        "accessory-tech-hosting-and-gateways-small-scale"       = "HostingAndGatewaysSmallScale"
        "accessory-tech-poe-and-power"                          = "PoEAndPower"
        "accessory-tech-poe-power"                              = "PoEPower"
        "accessory-tech-wifiman"                                = "WiFiManager"
        "internet-backup"                                       = "InternetBackup"
        "power-tech-power-redundancy"                           = "PowerRedundancy"
        "switching-pro-ethernet"                                = "ProEthernetSwitching"
        "switching-standard-ethernet"                           = "StandardEthernetSwitching"
        "switching-standard-power-over-ethernet"                = "StandardPoESwitching"
        "switching-utility-10-gbps-ethernet"                    = "10GbpsEthernetSwitching"
        "switching-utility-poe"                                 = "PoESwitching"
        "wifi-flagship-compact"                                 = "FlagshipCompactWiFi"
        "wifi-flagship-high-capacity"                           = "FlagshipHighCapacityWiFi"
        "wifi-inwall-high-capacity"                             = "InWallHighCapacityWiFi"
        "wifi-outdoor-flexible"                                 = "OutdoorFlexibleWiFi"

        "ui-care"                                               = "UICare"
    }

    $UrlStore = $Stores[$Store]
    $UrlStoreLink = $StoreLinks[$Store]

    $ProgressPreference = 'SilentlyContinue'
    try {
        Write-Verbose -Message "Get-UnifiStock - Getting Unifi products"
        $Output = Invoke-RestMethod -UseBasicParsing -Uri "https://ecomm.svc.ui.com/graphql" -Method Post -ContentType "application/json" -Body "{`"operationName`":`"GetProductsForLandingPagePro`",`"variables`":{`"input`":{`"limit`":250,`"offset`":0,`"filter`":{`"storeId`":`"$UrlStore`",`"language`":`"en`",`"line`":`"Unifi`"}}},`"query`":`"query GetProductsForLandingPagePro(`$input: StorefrontProductListInput!) {\n storefrontProducts(input: `$input) {\n pagination {\n limit\n offset\n total\n __typename\n }\n items {\n ...LandingProProductFragment\n __typename\n }\n __typename\n }\n}\n\nfragment LandingProProductFragment on StorefrontProduct {\n id\n title\n shortTitle\n name\n slug\n collectionSlug\n organizationalCollectionSlug\n shortDescription\n tags {\n name\n __typename\n }\n gallery {\n ...ImageOnlyGalleryFragment\n __typename\n }\n options {\n id\n title\n values {\n id\n title\n __typename\n }\n __typename\n }\n variants {\n id\n sku\n status\n title\n galleryItemIds\n isEarlyAccess\n optionValueIds\n displayPrice {\n ...MoneyFragment\n __typename\n }\n hasPurchaseHistory\n __typename\n }\n __typename\n}\n\nfragment ImageOnlyGalleryFragment on Gallery {\n id\n items {\n id\n data {\n __typename\n ... on Asset {\n id\n mimeType\n url\n height\n width\n __typename\n }\n }\n __typename\n }\n type\n __typename\n}\n\nfragment MoneyFragment on Money {\n amount\n currency\n __typename\n}`"}"
        $Products = $Output.data.storefrontProducts.items
    } catch {
        Write-Color -Text "Unable to get Unifi products. Error: $($_.Exception.Message)" -Color Red
        return
    }
    if ($Products) {
        Write-Verbose -Message "Get-UnifiStock - Got $($Products.Count) products"
        $UnifiProducts = foreach ($Product in $Products) {
            foreach ($Variant in $Product.variants) {
                if ($Product.collectionSlug) {
                    $Category = $Accessories[$Product.collectionSlug]
                } elseif ($Product.organizationalCollectionSlug) {
                    $Category = $Accessories[$Product.organizationalCollectionSlug]
                } else {
                    $Category = 'Unknown'
                }
                if ($Collection) {
                    if ($Category -notin $Collection) {
                        continue
                    }
                }
                [PSCustomObject] @{
                    Name                         = $Product.title
                    ShortName                    = $Product.shortTitle
                    Available                    = $Variant.status -eq 'AVAILABLE'
                    Category                     = $Category
                    Collection                   = $Product.collectionSlug
                    OrganizationalCollectionSlug = $Product.organizationalCollectionSlug
                    SKU                          = $Variant.sku
                    SKUName                      = $Variant.title
                    EarlyAccess                  = $Variant.isEarlyAccess
                    ProductUrl                   = "$UrlStoreLink/collections/$($Product.collectionSlug)/products/$($Product.slug)"
                    #Tags = $Product.tags
                }
            }
        }
        $UnifiProducts
    }
}
function Get-UnifiStockLegacy {
    <#
    .SYNOPSIS
    Get the stock status of Ubiquiti products from their online store.
 
    .DESCRIPTION
    Get the stock status of Ubiquiti products from their online store.
 
    .PARAMETER Store
    The store to check for stock. Valid values are: Europe, USA, Brazil, India, Japan, Taiwan, Signapore, Mexico, China
 
    .PARAMETER Collection
    Which collection to list.
 
    .EXAMPLE
    Get-UnifiStock -Store USA -Collection Protect, ProtectAccessories, ProtectNVR | Sort-Object -Property Name | Format-Table
 
    .EXAMPLE
    Get-UnifiStock -Store Europe -Collection Protect, NetworkWifi | Sort-Object -Property Name | Format-Table
 
    .EXAMPLE
    Get-UnifiStock -Store Europe | Sort-Object -Property Name | Format-Table
 
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    param(
        [ValidateSet('Brazil', 'India', 'Japan', 'Taiwan', 'Signapore', 'Mexico', 'China')]
        [Parameter(Mandatory)]
        [string] $Store,
        [ValidateSet(
            'EarlyAccess',
            'EarlyAccessConnect',
            'EarlyAccessDoorAccess',
            'EarlyAccessSmartpower',
            'EarlyAccessUispFiber',
            'EarlyAccessUispWired',
            'EarlyAccessUispWireless',
            'EarlyAccessUnifiNetworkHost',
            'NetworkHost',
            'NetworkOS',
            'NetworkRoutingOffload',
            'NetworkRoutingSwitching',
            'NetworkSmartPower',
            'NetworkSwitching',
            'NetworkWifi',
            'OperatorAirmaxAndLtu',
            'OperatorIspInfrastructure',
            'Protect',
            'ProtectAccessories',
            'ProtectNVR',
            'UnifiAccessories',
            'UnifiConnect',
            'UnifiDoorAccess',
            'UnifiPhoneSystem'
        )]
        [string[]] $Collection
    )

    $Stores = @{
        Europe    = 'https://eu.store.ui.com'
        USA       = 'https://store.ui.com'
        Brazil    = 'https://br.store.ui.com'
        India     = 'https://store-ui.in'
        Japan     = 'https://jp.store.ui.com'
        Taiwan    = 'https://tw.store.ui.com'
        Singapore = 'https://sg.store.ui.com'
        Mexico    = 'https://mx.store.ui.com'
        China     = 'https://store.ui.com.cn'
    }

    $Collections = @{
        Protect                     = 'unifi-protect'
        ProtectNVR                  = 'unifi-protect-nvr'
        ProtectAccessories          = 'unifi-protect-accessories'
        NetworkOS                   = 'unifi-network-unifi-os-consoles'
        NetworkRoutingSwitching     = 'unifi-network-routing-switching'
        NetworkSmartPower           = 'unifi-network-smartpower'
        NetworkRoutingOffload       = 'unifi-network-routing-offload'
        NetworkHost                 = 'unifi-network-host'
        NetworkSwitching            = 'unifi-network-switching'
        NetworkWifi                 = 'unifi-network-wireless'
        UnifiAccessories            = 'unifi-accessories'
        EarlyAccess                 = 'early-access'
        EarlyAccessDoorAccess       = 'early-access-door-access'
        EarlyAccessConnect          = 'early-access-connect'
        EarlyAccessSmartpower       = 'early-access-smartpower'
        EarlyAccessUispFiber        = 'early-access-uisp-fiber'
        EarlyAccessUispWired        = 'early-access-uisp-wired'
        EarlyAccessUispWireless     = 'early-access-uisp-wireless'
        EarlyAccessUnifiNetworkHost = 'early-access-unifi-network-host'
        UnifiConnect                = 'unifi-connect'
        UnifiDoorAccess             = 'unifi-door-access'
        OperatorAirmaxAndLtu        = 'operator-airmax -and -ltu'
        OperatorIspInfrastructure   = 'operator-isp-infrastructure'
        UnifiPhoneSystem            = 'unifi-phone-system'
    }

    $UrlStore = $Stores[$Store]

    if (-not $Collection) {
        $Collection = $Collections.Keys
    }

    foreach ($Category in $Collection) {
        $UrlCollection = $Collections[$Category]
        $Url = "$UrlStore/collections/$UrlCollection"
        $UrlProducts = "$Url/products.json"
        $ProgressPreference = 'SilentlyContinue'
        try {
            Write-Verbose -Message "Get-UnifiStock - Getting $UrlProducts"
            $Output = Invoke-WebRequest -Uri $UrlProducts -ErrorAction Stop -Verbose:$false
        } catch {
            Write-Color -Text "Unable to get $UrlProducts. Error: $($_.Exception.Message)" -Color Red
            return
        }
        if ($Output) {
            $OutputJSON = $Output.Content | ConvertFrom-Json
            $UnifiProducts = foreach ($Product in $OutputJSON.products) {
                foreach ($Variant in $Product.variants) {
                    try {
                        $DateCreated = [DateTime]::Parse($Variant.created_at)
                        #$DateCreated = [DateTime]::ParseExact($Variant.created_at, 'yyyy-MM-ddTHH:mm:sszzz', $null)
                    } catch {
                        Write-Verbose -Message "Unable to parse date: $($Variant.created_at). Skipping"
                        $DateCreated = $null
                    }
                    try {
                        $DateUpdated = [DateTime]::Parse($Variant.updated_at)
                        #$DateUpdated = [DateTime]::ParseExact($Variant.updated_at, 'yyyy-MM-ddTHH:mm:sszzz', $null)
                    } catch {
                        Write-Verbose -Message "Unable to parse date: $($Variant.updated_at). Skipping"
                        $DateUpdated = $null
                    }
                    [PSCustomObject] @{
                        Name       = $Product.title
                        Available  = $Variant.available
                        Category   = $Category
                        Price      = $Variant.price
                        SKU        = $Variant.sku
                        SKUName    = $Variant.title
                        #Inventory = $Variant.inventory_quantity
                        Created    = $DateCreated
                        Updated    = $DateUpdated
                        ProductUrl = "$Url/products/$($Product.handle)"
                        Tags       = $Product.tags
                    }
                }
            }
            $UnifiProducts
        }
    }
}
function Wait-UnifiStock {
    <#
    .SYNOPSIS
    When run waits for the specified SKU or Product to be in stock in Ubiquiti's online store.
 
    .DESCRIPTION
    When run waits for the specified SKU or Product to be in stock in Ubiquiti's online store.
    Once the product is in stock the function will play a beep, read which product is in stock and open a browser to specific product page.
 
    .PARAMETER ProductName
    One or more products to wait for to be in stock with search by it's Name
 
    .PARAMETER ProductSKU
    One or more products to wait for to be in stock with search by it's SKU
 
    .PARAMETER Store
    The store to check for stock. Valid values are Europe, USA and UK.
    If you want to use a different store you can use Wait-UnifiStockLegacy for other countries.
    This is because the legacy store has a different format for the JSON data, and are not yet migrated to new "look"
 
    .PARAMETER Seconds
    The number of seconds to wait between checks. Default is 60 seconds.
 
    .PARAMETER DoNotOpenWebsite
    If specified the website will not be opened when the product is in stock.
 
    .PARAMETER DoNotPlaySound
    If specified the sound will not be played when the product is in stock.
 
    .PARAMETER DoNotUseBeep
    If specified the beep will not be played when the product is in stock.
 
    .EXAMPLE
    Wait-UnifiStock -ProductSKU 'UDR-EU' -ProductName 'Switch Flex XG' -Seconds 60 -Store Europe
 
    .EXAMPLE
    Wait-UnifiStock -ProductName 'UniFi6 Mesh', 'G4 Doorbell Pro', 'Camera G4 Pro', 'Test' -Seconds 60 -Store Europe
 
    .EXAMPLE
    Wait-UnifiStock -ProductName 'UniFi6 Mesh', 'G4 Doorbell Pro', 'Camera G4 Pro', 'Test' -Seconds 60 -DoNotUseBeep -Store Europe
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [string[]] $ProductName,
        [string[]] $ProductSKU,
        [parameter(Mandatory)][ValidateSet('Europe', 'USA', 'UK')][string] $Store,
        [int] $Seconds = 60,
        [switch] $DoNotOpenWebsite,
        [switch] $DoNotPlaySound,
        [switch] $DoNotUseBeep
    )
    $Cache = [ordered] @{}
    $CurrentStock = Get-UnifiStock -Store $Store
    foreach ($Product in $CurrentStock) {
        $Cache[$Product.Name] = $Product
        $Cache[$Product.SKU] = $Product
    }

    [Array] $ApplicableProducts = @(
        foreach ($Name in $ProductName) {
            $Found = $false
            foreach ($StockName in $CurrentStock.Name) {
                if ($StockName -like "$Name") {
                    $StockName
                    $found = $true
                }
            }
            if (-not $Found) {
                Write-Color -Text "Product Name '$Name' not found in stock. Ignoring" -Color Red
            }
        }
        foreach ($Name in $ProductSKU) {
            if ($Name -in $CurrentStock.SKU) {
                $Name
            } else {
                Write-Color -Text "Product SKU '$Name' not found in stock. Ignoring" -Color Red
            }
        }
    )
    $ApplicableProducts = $ApplicableProducts | Sort-Object -Unique
    if ($ApplicableProducts.Count -eq 0) {
        Write-Color -Text "No products requested by user not found on list of available products. Exiting" -Color Red
        return
    }

    # $Collections = @(
    # foreach ($Product in $ApplicableProducts) {
    # $Cache[$Product].Category
    # }
    # ) | Select-Object -Unique

    Write-Color -Text "Setting up monitoring for ", ($ApplicableProducts -join ", ") -Color Yellow, Green
    $Count = 0
    Do {
        if ($Count -ne 0) {
            Start-Sleep -Seconds $Seconds
        }
        Write-Color -Text "Checking stock..." -Color Yellow
        $CurrentResults = Get-UnifiStock -Store $Store | Where-Object {
            $_.Name -in $ApplicableProducts -or $_.SKU -in $ApplicableProducts
        } | Sort-Object -Property Name
        Write-Color -Text "Checking stock... Done, sleeping for $Seconds seconds" -Color Green
        $Count++
    } While ($CurrentResults.Available -notcontains $true)

    foreach ($Product in $CurrentResults | Where-Object { $_.Available -eq $true }) {
        Write-Color -Text "Product ", $($Product.Name), " is in stock! ", "SKU: $($Product.SKU)" -Color Yellow, Green, Yellow, Green

        if (-not $DoNotOpenWebsite) {
            Start-Process $Product.ProductUrl
        }

        if (-not $DoNotPlaySound) {
            try {
                $Voice = New-Object -ComObject Sapi.spvoice -ErrorAction Stop
            } catch {
                Write-Color -Text "Failed to create voice object. Error: $($_.Exception.Message)" -Color Red
            }
            if ($Voice) {
                # Set the speed - positive numbers are faster, negative numbers, slower
                $voice.rate = 0

                # Say something
                try {
                    $null = $voice.speak("Hey,there is stock available for $($Product.Name)")
                } catch {
                    Write-Color -Text "Failed to speak. Error: $($_.Exception.Message)" -Color Red
                }
            }
        }
        if (-not $DoNotUseBeep) {
            [console]::beep(500, 300)
        }
    }
}
function Wait-UnifiStockLegacy {
    <#
    .SYNOPSIS
    When run waits for the specified SKU or Product to be in stock in Ubiquiti's online store.
 
    .DESCRIPTION
    When run waits for the specified SKU or Product to be in stock in Ubiquiti's online store.
    Once the product is in stock the function will play a beep, read which product is in stock and open a browser to specific product page.
 
    .PARAMETER ProductName
    One or more products to wait for to be in stock with search by it's Name
 
    .PARAMETER ProductSKU
    One or more products to wait for to be in stock with search by it's SKU
 
    .PARAMETER Store
    The store to check for stock. Valid values are Brazil, India, Japan, Taiwan, Signapore, Mexico, China
    If you want EU/USA store you can use Wait-UnifiStock for those.
 
    .PARAMETER Seconds
    The number of seconds to wait between checks. Default is 60 seconds.
 
    .PARAMETER DoNotOpenWebsite
    If specified the website will not be opened when the product is in stock.
 
    .PARAMETER DoNotPlaySound
    If specified the sound will not be played when the product is in stock.
 
    .PARAMETER DoNotUseBeep
    If specified the beep will not be played when the product is in stock.
 
    .EXAMPLE
    Wait-UnifiStockLegacy -ProductSKU 'UDR-EU' -ProductName 'Switch Flex XG' -Seconds 60 -Store Brazil
 
    .EXAMPLE
    Wait-UnifiStockLegacy -ProductName 'UniFi6 Mesh', 'G4 Doorbell Pro', 'Camera G4 Pro', 'Test' -Seconds 60 -Store Brazil
 
    .EXAMPLE
    Wait-UnifiStockLegacy -ProductName 'UniFi6 Mesh', 'G4 Doorbell Pro', 'Camera G4 Pro', 'Test' -Seconds 60 -DoNotUseBeep -Store Brazil
 
    .NOTES
    General notes
    #>

    [cmdletBinding()]
    param(
        [string[]] $ProductName,
        [string[]] $ProductSKU,
        [parameter(Mandatory)][ValidateSet('Brazil', 'India', 'Japan', 'Taiwan', 'Signapore', 'Mexico', 'China')][string] $Store,
        [int] $Seconds = 60,
        [switch] $DoNotOpenWebsite,
        [switch] $DoNotPlaySound,
        [switch] $DoNotUseBeep
    )
    $Cache = [ordered] @{}
    $CurrentStock = Get-UnifiStockLegacy -Store $Store
    foreach ($Product in $CurrentStock) {
        $Cache[$Product.Name] = $Product
        $Cache[$Product.SKU] = $Product
    }

    [Array] $ApplicableProducts = @(
        foreach ($Name in $ProductName) {
            $Found = $false
            foreach ($StockName in $CurrentStock.Name) {
                if ($StockName -like "$Name") {
                    $StockName
                    $found = $true
                }
            }
            if (-not $Found) {
                Write-Color -Text "Product Name '$Name' not found in stock. Ignoring" -Color Red
            }
        }
        foreach ($Name in $ProductSKU) {
            if ($Name -in $CurrentStock.SKU) {
                $Name
            } else {
                Write-Color -Text "Product SKU '$Name' not found in stock. Ignoring" -Color Red
            }
        }
    )
    if ($ApplicableProducts.Count -eq 0) {
        Write-Color -Text "No products requested by user not found on list of available products. Exiting" -Color Red
        return
    }

    $Collections = @(
        foreach ($Product in $ApplicableProducts) {
            $Cache[$Product].Category
        }
    ) | Select-Object -Unique

    Write-Color -Text "Setting up monitoring for ", ($ApplicableProducts -join ", ") -Color Yellow, Green
    $Count = 0
    Do {
        if ($Count -ne 0) {
            Start-Sleep -Seconds $Seconds
        }
        Write-Color -Text "Checking stock..." -Color Yellow
        $CurrentResults = Get-UnifiStockLegacy -Store $Store -Collection $Collections | Where-Object {
            $_.Name -in $ApplicableProducts -or $_.SKU -in $ApplicableProducts
        } | Sort-Object -Property Name
        Write-Color -Text "Checking stock... Done, sleeping for $Seconds seconds" -Color Green
        $Count++
    } While ($CurrentResults.Available -notcontains $true)

    foreach ($Product in $CurrentResults | Where-Object { $_.Available -eq $true }) {
        Write-Color -Text "Product ", $($Product.Name), " is in stock! ", "SKU: $($Product.SKU)" -Color Yellow, Green, Yellow, Green

        if (-not $DoNotOpenWebsite) {
            Start-Process $Product.ProductUrl
        }

        if (-not $DoNotPlaySound) {
            try {
                $Voice = New-Object -ComObject Sapi.spvoice -ErrorAction Stop
            } catch {
                Write-Color -Text "Failed to create voice object. Error: $($_.Exception.Message)" -Color Red
            }
            if ($Voice) {
                # Set the speed - positive numbers are faster, negative numbers, slower
                $voice.rate = 0

                # Say something
                try {
                    $null = $voice.speak("Hey,there is stock available for $($Product.Name)")
                } catch {
                    Write-Color -Text "Failed to speak. Error: $($_.Exception.Message)" -Color Red
                }
            }
        }
        if (-not $DoNotUseBeep) {
            [console]::beep(500, 300)
        }
    }
}


# Export functions and aliases as required
Export-ModuleMember -Function @('Get-UnifiStock', 'Get-UnifiStockLegacy', 'Wait-UnifiStock', 'Wait-UnifiStockLegacy') -Alias @()
# SIG # Begin signature block
# MIItrwYJKoZIhvcNAQcCoIItoDCCLZwCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCLb1umrCr9GBDu
# au3uIJ3SAYDTRMA3n+Njzmny/8XCxKCCJrIwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggWQMIIDeKADAgECAhAFmxtXno4hMuI5B72nd3VcMA0GCSqG
# SIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
# GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy
# dXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBH
# NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIw
# aTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLK
# EdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4Tm
# dDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembu
# d8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnD
# eMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1
# XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVld
# QnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTS
# YW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSm
# M9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzT
# QRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6Kx
# fgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD
# VR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzANBgkq
# hkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNkaA9Wz3eucPn9mkqZucl4
# XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjSPMFDQK4dUPVS/JA7u5iZ
# aWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK7VB6fWIhCoDIc2bRoAVg
# X+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eBcg3AFDLvMFkuruBx8lbk
# apdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp5aPNoiBB19GcZNnqJqGL
# FNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msgdDDS4Dk0EIUhFQEI6FUy
# 3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vriRbgjU2wGb2dVf0a1TD9u
# KFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ79ARj6e/CVABRoIoqyc54
# zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5nLGbsQAe79APT0JsyQq8
# 7kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3i0objwG2J5VT6LaJbVu8
# aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0HEEcRrYc9B9F1vM/zZn4w
# 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/bt1nz8MIIGsDCCBJigAwIBAgIQ
# CK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjEwNDI5MDAw
# MDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBT
# aWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjANBgkqhkiG9w0BAQEF
# AAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zrPYGXcMW7xIUmMJ+k
# jmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHMgQM+TXAkZLON4gh9
# NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8IrgnQnAZaf6mIBJNYc9
# URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyCEUhSaN4QvRRXXegY
# E2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0p6MDDnSlrzm2q2AS
# 4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQakhCBj7A7CdfHmzJa
# wv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0XLyTRSiDNipmKF+w
# c86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960IHnWmZcy740hQ83eR
# Gv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2FKZbS110YU0/EpF2
# 3r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBHX8mBUHOFECMhWWCK
# ZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q27IwyCQLMbDwMVhEC
# AwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFGg34Ou2
# O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9P
# MA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB3BggrBgEFBQcB
# AQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggr
# BgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1
# c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwHAYDVR0gBBUwEzAH
# BgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBADojRD2NCHbuj7w6
# mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6jfCbVN7w6XUhtldU/
# SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmImoqKwba9oUgYftzY
# gBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtfJqGVWEjVGv7XJz/9
# kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrxoj7bQ7gzyE84FJKZ
# 9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3LIU/Gs4m6Ri+kAew
# Q3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx4b6cpwoG1iZnt5Lm
# Tl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9Oj9FpsToFpFSi0HA
# SIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+ICw2/O/TOHnuO77Xr
# y7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug0wcCampAMEhLNKhR
# ILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5Vzu0nAPthkX0tGFu
# v2jiJmCG6sivqf6UHedjGzqGVnhOMIIGwDCCBKigAwIBAgIQDE1pckuU+jwqSj0p
# B4A9WjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln
# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5
# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIyMDkyMTAwMDAwMFoXDTMzMTEy
# MTIzNTk1OVowRjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCERpZ2lDZXJ0MSQwIgYD
# VQQDExtEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMiAtIDIwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDP7KUmOsap8mu7jcENmtuh6BSFdDMaJqzQHFUeHjZt
# vJJVDGH0nQl3PRWWCC9rZKT9BoMW15GSOBwxApb7crGXOlWvM+xhiummKNuQY1y9
# iVPgOi2Mh0KuJqTku3h4uXoW4VbGwLpkU7sqFudQSLuIaQyIxvG+4C99O7HKU41A
# gx7ny3JJKB5MgB6FVueF7fJhvKo6B332q27lZt3iXPUv7Y3UTZWEaOOAy2p50dIQ
# kUYp6z4m8rSMzUy5Zsi7qlA4DeWMlF0ZWr/1e0BubxaompyVR4aFeT4MXmaMGgok
# vpyq0py2909ueMQoP6McD1AGN7oI2TWmtR7aeFgdOej4TJEQln5N4d3CraV++C0b
# H+wrRhijGfY59/XBT3EuiQMRoku7mL/6T+R7Nu8GRORV/zbq5Xwx5/PCUsTmFnta
# fqUlc9vAapkhLWPlWfVNL5AfJ7fSqxTlOGaHUQhr+1NDOdBk+lbP4PQK5hRtZHi7
# mP2Uw3Mh8y/CLiDXgazT8QfU4b3ZXUtuMZQpi+ZBpGWUwFjl5S4pkKa3YWT62SBs
# GFFguqaBDwklU/G/O+mrBw5qBzliGcnWhX8T2Y15z2LF7OF7ucxnEweawXjtxojI
# sG4yeccLWYONxu71LHx7jstkifGxxLjnU15fVdJ9GSlZA076XepFcxyEftfO4tQ6
# dwIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYD
# VR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZI
# AYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQW
# BBRiit7QYfyPMRTtlwvNPSqUFN9SnDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8v
# Y3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2
# VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hB
# MjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQBVqioa80bz
# eFc3MPx140/WhSPx/PmVOZsl5vdyipjDd9Rk/BX7NsJJUSx4iGNVCUY5APxp1Mqb
# KfujP8DJAJsTHbCYidx48s18hc1Tna9i4mFmoxQqRYdKmEIrUPwbtZ4IMAn65C3X
# CYl5+QnmiM59G7hqopvBU2AJ6KO4ndetHxy47JhB8PYOgPvk/9+dEKfrALpfSo8a
# OlK06r8JSRU1NlmaD1TSsht/fl4JrXZUinRtytIFZyt26/+YsiaVOBmIRBTlClmi
# a+ciPkQh0j8cwJvtfEiy2JIMkU88ZpSvXQJT657inuTTH4YBZJwAwuladHUNPeF5
# iL8cAZfJGSOA1zZaX5YWsWMMxkZAO85dNdRZPkOaGK7DycvD+5sTX2q1x+DzBcNZ
# 3ydiK95ByVO5/zQQZ/YmMph7/lxClIGUgp2sCovGSxVK05iQRWAzgOAj3vgDpPZF
# R+XOuANCR+hBNnF3rf2i6Jd0Ti7aHh2MWsgemtXC8MYiqE+bvdgcmlHEL5r2X6cn
# l7qWLoVXwGDneFZ/au/ClZpLEQLIgpzJGgV8unG1TnqZbPTontRamMifv427GFxD
# 9dAq6OJi7ngE273R+1sKqHB+8JeEeOMIA11HLGOoJTiXAdI/Otrl5fbmm9x+LMz/
# F0xNAKLY1gEOuIvu5uByVYksJxlh9ncBjDCCB18wggVHoAMCAQICEAfCUnQoFKLW
# q/4k6hfl3S4wDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
# DkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENv
# ZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMTAeFw0yMzA0MTYwMDAw
# MDBaFw0yNjA3MDYyMzU5NTlaMGcxCzAJBgNVBAYTAlBMMRIwEAYDVQQHDAlNaWtv
# xYLDs3cxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlzIEVWT1RFQzEhMB8GA1UE
# AwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIICIjANBgkqhkiG9w0BAQEFAAOC
# Ag8AMIICCgKCAgEAlJoHlzELSGimkpCr2wLfBhWSdcsDh/EsMZU7rODHMq1plTq0
# QVUUAPAKRfRWnqG8JpGcb5MUExSxypvvJJ8KJhFLJXGvAqkjiNGMBC7+RME1RIdA
# vw2nob8aOrZJjTxff0j9Sgt3NJdbzvjO73TVRikCEK4cauxBtInswWTgIrpDXRlV
# 0WDi5+O1d6i+T8Bv6LtmpSf74nyA2nfNahW/kJFIdNiaNuEjI1nSg8rXazF4tNt+
# QjeEa1vvII30Sfnyio4DCJm7nHgrIvSL9Wuum1HPWpwHpjm0+JheVP8kAYALgKN/
# o1QfMIlHfO5FEDtMyQhfL6tmK1Ts/DiZjF/IICLBBFGdwmSg9IVXN3Zu3FkgMPPx
# TcxjT5QGiMc11/ang9BIGgi0ZCLQN7d3kFviAF8kv/WZ56RVKA70BmyvkOP2z9Im
# /fFy30KcVRkbtHAldDYO+wyJERfiMkdT3MFQKvjs1VN7ynqNub/657YlwpgsYluK
# B2DtvHkkP3iAHJ4ovt7igzWayNeT+1cQ65FCHOhbYkrzocHNwM2PrxH4r1JBSkas
# L0kq+Hwq65JO89kHu9mcJcNhA0VR8stH1FRjvUDLoehN0cJyS/eoqdGpXJoSgARq
# CKkltOZ13QlG5F5oTwk0+Z2kA7mdVJAF22T0oSo2z8M3Vz9m/CPZ0PPVUoECAwEA
# AaOCAgMwggH/MB8GA1UdIwQYMBaAFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1Ud
# DgQWBBR68WolWbgyccRJNeWy6DLhSOdt9zAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0l
# BAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2
# U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFD
# QTEuY3JsMD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6
# Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCBlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0
# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNp
# Z25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcnQwCQYDVR0TBAIwADANBgkqhkiG
# 9w0BAQsFAAOCAgEAtxHh11D4aXt9Stgy+Nx34eqpLwR8kdUZQ/ZVSJJXEQkedGR8
# 6FrOhAZUxcqIb5KXJVQrkXUFt97Uur7SjzrnKQw7+MLAPus5CWCPHx6Lluk6mtVu
# O2Eq3OQDkoSHCffjaTWyjRood3aEpXIqNplCgl+SP2a8yQZEKSdJGIWv6VEk9gmx
# Nya6CX9r0FhlIiPidy3YjzR5oTtZfs2kJEsb9HFQxEzH0BmSikVREmehYOtW9HY7
# 0EseddDHW8bSjI70t2bQMrap0B5NYqT/kYPjOZRR60pFJZ6Rmvn957kIcQ2+zfRP
# IVFXr8QC06xYn4PM4bJVUR+fw3/wsZTClwu6Kd9PwMkLDkMR1tbjcd7RtQzIIs6c
# AWrK8YesGu4mgPi6dO6tSPdni4a2G7cN8QtrzSBnTHTe53e+sjCI3WJwJ+69/MML
# WidymA9EE5e+xAfLv+XArN0oWXQ3coOCuzaCZfIhB626raKABzjC4iaYi9ovWJ/J
# EDAev0OkTDtyFDy7snAfaOgzYsEB3+ibeaFuz9PZOTccQRJpLMcDW5mbzUOuWZ93
# sVACqhvsd9RIM+SGeFP4z80WpRJRCKUtK4K1YPEfKRDoXfeZhM6eVhEShcl4Xupw
# em0mB7/HJSwFdIjJLt9PK6X4zIkJKktyy831CeTh6rSikDTC8c/c9fOVArMxggZT
# MIIGTwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j
# LjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNB
# NDA5NiBTSEEzODQgMjAyMSBDQTECEAfCUnQoFKLWq/4k6hfl3S4wDQYJYIZIAWUD
# BAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMx
# DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq
# hkiG9w0BCQQxIgQgANt8RCyZkkE1deXL99W8Fomcvp3K5SP6R2SJU9meYIMwDQYJ
# KoZIhvcNAQEBBQAEggIADIVGPFkGdXh40CuAtf5fmM5euAN3WJKdeAJXJSBmufzX
# LG6FQmjygCMBO8L3kG5EdRSZ8/UDErMzGA5oX6NHylsHqvAMU0FKEWF5jzf8j5J6
# nR1/GOw2NqOLTtPqt8tbOJmH6W24xnyjBQpHHpp5RNXjam67K8H0nRW5eW9mhF62
# C3Y8C3E7/B0I7UA532lXUwj1GejCxz1JaRonRHRVRtxrluNow1JbBGChCuh/iPBY
# MyvcOXytsPUxSM+rRyF7az9a35VoKrZ/aO7sfSeLMbsD0hlo3DhT2oy2Jt8tt/5A
# NfFzkjWWrrtFnjCBUaKD7VRPRTYu3PO3WdAEB0G3g5Q/HiE8n+PIGMcbCPxTtz/c
# 6E1akPgw4EnFwGpTLW7x2YBtXsb0INhNt5mSOuIw/6044/CrYUy6q2DOW5eE6UdD
# wMLxj/bC4/T3Sr2AgLxF6LLaWlFGWaWX1vazzWt0rkbM3/1lxIZMID0JZZ3+noxp
# YZYepHjaXlzJdFFN7lToECJpjmROeDTFngjqrgTXCX9SfBUrAKrB9DuD3+4ap/Qj
# EPoQ9B/B32cuk4Kj/oCMsBo592hM+jk5lF0Ab99vGHFkyqqGsvYNoBKIKQasMLgO
# jnm1iO3ZZFinu0OT4eVAFzKATuq6UZ0L39GP7SY/2XN7z9TuppyNMhNAjyD1BMKh
# ggMgMIIDHAYJKoZIhvcNAQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEX
# MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0
# ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAMTWlyS5T6PCpK
# PSkHgD1aMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEH
# ATAcBgkqhkiG9w0BCQUxDxcNMjMwNzI0MDY1NTI2WjAvBgkqhkiG9w0BCQQxIgQg
# yRU1qLkz7+0SHKZk03veRyvGYkeZUyHcl0Ygmv1ouW0wDQYJKoZIhvcNAQEBBQAE
# ggIArjLjWAvqu7DC1wOWVXYbOs2FtyA6bobBeWbNVPMuAYsIG2Lu8FSHaax51Mbr
# V08o83ibkTcDnXnuwnHC4/E3ljyl1rniOJOOWgXjtCHUhvODYiED7et0+QvfGTjb
# jo963U1ErCaIUecYboPBGqW4hSTtXHACmdZUIW4fay8PJ1eQgDYMAjhUMV1pLviJ
# xYSpquI/bS82Q6dSWWq192IUIphMZP63VaHHW/0K7FMaoZYLIkdpY1y/8gRSz1f/
# 6JVM/xgAbDYPP+vn1bd4R3LP/Xn0o54d4aucBqFRtK7dd/yILeoolNTDh5A9MZcm
# MHcYPf7Z8tHd/+tbMPx76ttoJDysVcuLWEh/ALfTWbqD1VAR9+LmBWfzdYf1Kt2h
# AaLo17fjdwg4BMuEObCftVatqhoqsxalqLi44ff4TwwXzjOkw/Yd02eJONEFnq72
# DCKibHKhEIK8R36IRJZNkDtEij3kMMkQnhjTFfQB7lgQqnJhVyGWcvPOOet7kyFJ
# LPMwSiXmgiFLduZ6OrrZi4CzwEVdi3gZOXoNiU/JJhDwgt7LtrkCHLsOFxszL8R8
# 0C6eN2sPSu2Vw0faGBPcPgOGrzoPwgyXep1HbbVBqckYQtqucwy/lOQ4hWxKcXhQ
# p/DsfHB9OOlW6S9r0dStbfCXNDRNGEhZ9VxExt8qnPaAy1g=
# SIG # End signature block